How to prevent Image.FromFile () from locking a file

I use the following code to put JPG in a DataGridView Image cell.

 If strFileName.ToLower.EndsWith(".jpg") Then Dim inImg As Image = Image.FromFile(strFileName) DataGridView4.Rows.Add() DataGridView4.Rows(DataGridView4.Rows().Count - 1).Cells(0).Value = inImg End If 

The problem is that I need to save this file from the program, but I get a message that the file is being used by another program .

So, I tried to add inImg.Dispose() to the end of the if, but then the program no longer displays the images in the DataGridView .

How to add images to a DataGridView without locking them?

thanks

+7
locking image save datagridview
source share
4 answers

When you use the Image.FromFile(strFileName) method to create an Image , the method locks the file until you release Image . The exact reason is explained below. And why you cannot access the same image file more than once using this method.

Instead, you can:

Here it is possible to implement the SafeImageFromFile user method, which does not block 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 

Using

 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 a FileStream or MemoryStream using the Using statement to make sure the stream is released. It works fine on my system and it seems to work for you, although MSDN is talking about Image.FromStream (stream) :

You must leave the stream open for the life of the image.

The reason for this proposal is explained here: KB814675 Dependencies of raster image designer and image constructor

GDI +, and so the System.Drawing namespace can delay decoding the original image bits until a bit is needed for the image. Furthermore, even after the image has been decoded, GDI + can determine that it is more efficient to discard the memory for a large bitmap and re-decode later. Therefore, GDI + must have access to the source bits for the image for the life of the bitmap or image object.

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

Be aware that the code above can generate GDIexceptions due to the release of the thread using Using . This can happen when you save an image from a file or during image creation. From this topic Downloading images from a stream without saving the stream and comment Hans Passant fixed several problems with indexed pixel formats in the version of gdiplus.dll Vista, this will happen only on XP.

To avoid this, you need to leave the stream open. The methods will 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 these latter methods have some drawbacks like not freeing up the thread (memory problem), and they violate the CA2000 rule of Utility objects before losing visibility .

The KB article provides some workarounds:

Create unindexed image

This approach requires that the new image be in an unindexed 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:

  • Build the original bitmap from a stream, from memory, or from a file.
  • Create a new Bitmap of the same size, with a pixel format of more than 8 bits per pixel (BPP).
  • Use the Graphics.FromImage () method to get the Graphics object for the second bitmap.
  • Use Graphics.DrawImage () to draw the first bitmap onto the second bitmap.
  • Use Graphics.Dispose () to remove graphics.
  • Use Bitmap.Dispose () to get rid of the first bitmap.

Create Indexed Image

This workaround creates a Bitmap object in indexed format:

  • Build the original bitmap from a stream, from memory, or from a file.
  • Create a new bitmap with the same size and pixel size as the first bitmap.
  • Use the Bitmap.LockBits () method to lock the entire image for both Bitmap objects in their own pixel format.
  • Use either the Marshal.Copy function or another memory copy function to copy an image bit from the first bitmap to the second bitmap.
  • Use the Bitmap.UnlockBits () method to unlock both Bitmap objects. Use Bitmap.Dispose () to get rid of the first bitmap.

The following is an implementation for creating non-indexed images based on KB article and this answer https://stackoverflow.com/a/4646262/2126323# The best way is to create a perfect copy of the image - although YMMV (with certain types of images there may be more than one frame, or you may have to copy palette data). 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 pointed out that the important thing is that the FileStream opens in read mode ( FileAccess.Read ).

True , but it makes sense if you do not use the Using statement, and therefore you do not release the stream or in the context of several streams: FileAccess.Write not suitable, and FileAccess.ReadWrite not required, but it FileAccess.Read not hurt to open the stream using the FileAccess.Read mode IO.Exception , if another program (or yours in the context of several threads) opened a file with a different mode than FileAccess.Read .


If you want to be able to display an image and at the same time save data to a file, since you are not blocking the file using these methods, you should be able to save the image (delete / overwrite the previous file) using the Image.Save method.

+17
source share

@Chris: Having opened approximately 100 large (3400x2200) images with your final code, I got an invalid argument failure on [img = new bitmap(...] , I saw this before opening a zero-sized image, but here it wasn’t , I added fs.dispose and successfully opened thousands of images of the same size as the first test without any problems. I am interested in your comments on this.

 Private Function SafeImageFromFile(FilePath As String) As Image Dim img As Bitmap = Nothing Using fs As New FileStream(FilePath, 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 fs.Dispose() End Using Return img End Function 
0
source share

This works without problems, without problems starts 4189 images 3400x2200 (twice), it moves the stream outside the function and reuses it. Im closing the file to release write lock. Im pointing the image on this image in a loop for my test.

 Private fsIMG As FileStream Private Function SafeImageFromFile(FilePath As String) As Image 'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file Dim img As Bitmap = Nothing fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read) Using b As New Bitmap(fsIMG) 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 fsIMG.Close() Return img End Function 
0
source share

After a long search on the Internet, I found out that I can use this code without errors.

  Private fsIMG As FileStream Private Function SafeImageFromFile(FilePath As String) As Image 'Ref: http://stackoverflow.com/questions/18250848/how-to-prevent-the-image-fromfile-method-to-lock-the-file Dim img As Bitmap = Nothing fsIMG = New FileStream(FilePath, FileMode.Open, FileAccess.Read) Using b As New Bitmap(fsIMG) 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 fsIMG.Close() Return img End Function 
0
source share

All Articles