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.