Free File Locked by New Bitmap(Filepath)

Free file locked by new Bitmap(filePath)

Using a filestream will unlock the file once it has been read from and disposed:

using (var fs = new System.IO.FileStream("c:\\path to file.bmp", System.IO.FileMode.Open))
{
var bmp = new Bitmap(fs);
pct.Image = (Bitmap) bmp.Clone();
}

Edit: Updated to allow the original bitmap to be disposed, and allow the FileStream to be closed.

THIS ANSWER IS NOT SAFE - See comments, and see discussion in net_prog's answer. The Edit to use Clone does not make it any safer - Clone clones all fields, including the filestream reference, which in certain circumstances will cause a problem.

How do I release a file held by BitMap class

Try

using(var image = new Bitmap(filepath))
{
image.Save(someOtherPath);
}


File.Delete(filePath);
File.Move(someOtherPath, filePath);

Open Image from file, then release lock?

The approach with stream is not correct.

See here https://stackoverflow.com/a/8701748/355264

Correct code from above link:

Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}

how to prevent the Image.FromFile() method to lock the file

When you use the Image.FromFile(strFileName) method to create the Image, the method locks the file until you release the Image. The exact reason is explained below. And it's why you can't access more than one time to the same image file with this method.

You could instead:

  • use the Image.FromStream(stream) method.
  • that you use with a New FileStream or a MemoryStream that you create from the image file.

Here are possible implementation of a custom SafeImageFromFile method that doesn't lock the image file:

Public Shared Function SafeImageFromFile(path As String) As Image
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End using
End Function

Or

Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Using ms As New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Using
End Function

Usage

If strFileName.ToLower.EndsWith(".jpg") Then
Dim inImg As Image = SafeImageFromFile(strFileName)
Dim index as integer = DataGridView4.Rows.Add()
DataGridView4.Rows(index).Cells(0).Value = inImg
End If

Important note

Here I create the FileStream or a MemoryStream using a Using statement to make sure the stream is released. It works fine on my system and it seems it work for you too, though MSDN says about Image.FromStream(stream) method:

You must keep the stream open for the lifetime of the Image.

The reason of this sentence is explain here: KB814675 Bitmap and Image constructor dependencies

GDI+, and therefore the System.Drawing namespace, may defer the
decoding of raw image bits until the bits are required by the image.

Additionally, even after the image has been decoded, GDI+ may
determine that it is more efficient to discard the memory for a large
Bitmap and to re-decode later.
Therefore, GDI+ must have access to the
source bits for the image for the life of the Bitmap or the Image
object.

To retain access to the source bits, GDI+ locks any source file, and
forces the application to maintain the life of any source stream, for
the life of the Bitmap or the Image object.

So know the code above could generate GDIexceptions because of releasing the stream using Using. It could happen when you save the image from the file or during the image creation. From this thread Loading an image from a stream without keeping the stream open and Hans Passant's comment they fixed several problems with indexed pixel formats in the Vista version of gdiplus.dll., it would happen only on XP.

To avoid this you need to keep the stream open. The methods would be:

Public Shared Function SafeImageFromFile(path As String) As Image
Dim fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Dim img = Image.FromStream(fs)
Return img
End Function

Or

Public Shared Function SafeImageFromFile(path As String) As Image
Dim bytes = File.ReadAllBytes(path)
Dim ms = New MemoryStream(bytes)
Dim img = Image.FromStream(ms)
Return img
End Function

But those last methods have some disadvantage like not releasing the stream (memory issue) and they violate rule CA2000 Dispose objects before losing scope .

The KB article gives some workarounds:

Create a Non-Indexed Image

This approach requires that the new image be in a non-indexed pixel
format (more than 8 bits-per-pixel), even if the original image was in
an indexed format. This workaround uses the Graphics.DrawImage()
method to copy the image to a new Bitmap object:

  1. Construct the original Bitmap from the stream, from the memory, or from the file.
  2. Create a new Bitmap of the same size, with a pixel format of more than 8 bits-per-pixel (BPP).
  3. Use the Graphics.FromImage() method to obtain a Graphics object for the second Bitmap.
  4. Use Graphics.DrawImage() to draw the first Bitmap onto the second Bitmap.
  5. Use Graphics.Dispose() to dispose of the Graphics.
  6. Use Bitmap.Dispose() to dispose of the first Bitmap.

Create an Indexed Image

This workaround creates a Bitmap object in an indexed format:

  1. Construct the original Bitmap from the stream, from the memory, or from the file.
  2. Create a new Bitmap with the same size and pixel format as the first Bitmap.
  3. Use the Bitmap.LockBits() method to lock the whole image for both Bitmap objects in their native pixel format.
  4. Use either the Marshal.Copy function or another memory copying function to copy the image bits from the first Bitmap to the second Bitmap.
  5. Use the Bitmap.UnlockBits() method to unlock both Bitmap objects.
    Use Bitmap.Dispose() to dispose of the first Bitmap.

Here is an implementation of Non-Indexed Image creation, based on KB article and this answer https://stackoverflow.com/a/7972963/2387010 Your best bet is creating a pixel-perfect replica of the image -- though YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data as well.) But for most images, this works:

Private Shared Function SafeImageFromFile(path As String) As Bitmap
Dim img As Bitmap = Nothing
Using fs As New FileStream(path, FileMode.Open, FileAccess.Read)
Using b As New Bitmap(fs)
img = New Bitmap(b.Width, b.Height, b.PixelFormat)
Using g As Graphics = Graphics.FromImage(img)
g.DrawImage(b, Point.Empty)
g.Flush()
End Using
End Using
End Using
Return img
End Function

Someone indicated that what is important is that the FileStream is opened in read mode (FileAccess.Read).

True, but it makes more sens if you don't use Using statement and so you don't release the stream, or in multi threads context: FileAccess.Write is inappropriate, and FileAccess.ReadWrite is not required, but open the stream with FileAccess.Read mode won't prevent to have an IO.Exception if another program (or yours in multi threads context) has opened the file with another mode than FileAccess.Read.


If you want to be able to display the image and at the same time be able to save data to the file, Since you don't lock the file with those methods, you should be able to save the image (delete/overwrite the previous file) using the Image.Save method.

lock image file in PictureBox

Image.FromFile will keep the File open which prevents access to the image file till the Image is disposed. If you want to release the lock, you need to keep the Image file in memory.

myPictureBox.Image = Image.FromStream(new MemoryStream(File.ReadAllBytes(strImageFile)));

File.Delete on bitmap doesn't work because the file is being used by another process (I used bitmap.Dispose();)

It's not the Bitmap that causes the problem, but this line:

Buttons.BackgroundImage = Image.FromFile(name);

Here, you create an instance of an Image class using the factory method FromFile(). The file remains locked until the Image is disposed.

This is the reference.

Loading a file to a Bitmap but leaving the original file intact

Some background info on this behavior: Bitmap uses a memory-mapped file to access the pixels in the bitmap. That's a very basic facility in the Windows API, it allows very efficient mapping of memory to file data. Data is read from the file only when the program read the memory, the virtual memory pages don't take any space in the Windows paging file.

The exact same mechanism is used to load .NET assemblies. It is the memory mapping that puts a lock on the file. Which is basically why assemblies are locked when they are used in a .NET program. The Image.Dispose() method releases the lock. Fighting the lock often indicates that you are forgetting to dispose your bitmaps. Very important, forgetting to call Dispose() doesn't often cause problems for .NET classes, except for Bitmap since it can need so much (unmanaged) memory.

Yes, FromStream() prevents the class from making this optimization. The cost is significant, you'll need double the memory when the bitmap is loaded. This will be a problem when the bitmap is large, you're skirting OOM when the program has been running for a while (fragmenting the address space) and its not running on a 64-bit operating system. Definitely avoid doing this if the bitmap's Width x Height x 4 >= 45 MB, give or take.

Some code, you don't have to jump through the CopyStream hoop:

    public static Image LoadImageNoLock(string path) {
var ms = new MemoryStream(File.ReadAllBytes(path)); // Don't use using!!
return Image.FromStream(ms);
}

Note that you don't want to dispose the MemoryStream, you'll get a hard to diagnose "generic error" when the bitmap gets used if you do. Caused by the Image class lazy-reading the stream.

Overwrite BMP file, can't delete, because it's used by another process - Dispose, using not working

The line

using (var bmp = new Bitmap((Bitmap)Image.FromFile(file)))

Loads a bitmap from the file, then creates an independent copy of it using the Bitmap(Image) constructor. Upon exiting the using statement the copy will get disposed -- but not inner bitmap loaded from the file. Until that inner bitmap is eventually finalized by the GC it will maintain a lock on the file as is stated in the docs:

The file remains locked until the Image is disposed.

This prevents you from deleting the file right away.

Assuming you are actually trying to modify the image in the file and save it back to the original location, you could do something like:

Bitmap bmp = null;
try
{
using (var bmpFromFile = (Bitmap)Image.FromFile(file))
{
bmp = new Bitmap(bmpFromFile);
}

using (var g = Graphics.FromImage(bmp))
{
// Make changes to bmp.
}

// Save bmp to a temp file.

// Delete the original file and move the temp file to that name.
}
finally
{
// Dispose bmp
using (bmp) { }
}

Alternatively, load the file into an intermediate MemoryStream then create the bitmap from the memory stream as suggested here.



Related Topics



Leave a reply



Submit