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.