Free file locked by new Bitmap(filePath)

I have the Image of a PictureBox pointing to a certain file "A". At execution time I want to change the Image of the PictureBox to a different one "B" but I get the following error:

"A first chance exception of type 'System.IO.IOException' occurred in mscorlib.dll Additional information: The process cannot access the file "A" because it is being used by another process."

I'm setting the Image as follows:

pbAvatar.Image = new Bitmap(filePath);

How can I unlock the first file?


Here is my approach to opening an image without locking the file...

public static Image FromFile(string path)
{
    var bytes = File.ReadAllBytes(path);
    var ms = new MemoryStream(bytes);
    var img = Image.FromStream(ms);
    return img;
}

UPDATE: I did some perf tests to see which method was the fastest. I compared it to @net_progs "copy from bitmap" answer (which seems to be the closest to correct, though does have some issues). I loaded the image 10000 times for each method and calculated the average time per image. Here are the results:

Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.

The results seem to make sense since you have to create the image twice using the copy from bitmap method.

UPDATE: if you need a BitMap you can do:

return (Bitmap)Image.FromStream(ms);

This is a common locking question widely discussed over the web.

The suggested trick with stream will not work, actually it works initially, but causes problems later. For example, it will load the image and the file will remain unlocked, but if you try to save the loaded image via Save() method, it will throw a generic GDI+ exception.

Next, the way with per pixel replication doesn't seem to be solid, at least it is noisy.

What I found working is described here: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile--locks-file.aspx

This is how the image should be loaded:

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

I was looking for a solution to this problem and this method works fine for me so far, so I decided to describe it, since I found that many people advise the incorrect stream approach here and over the web.


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.


You can't dispose / close a stream while a bitmap object is still using it. (Whether the bitmap object will need access to it again is only deterministic if you know what type of file you are working with and exactly what operations you will be performing. -- for example for SOME .gif format images, the stream is closed before the constructor returns.)

Clone creates an "exact copy" of the bitmap (per documentation; ILSpy shows it calling native methods, so it's too much to track down right now) likely, it copies that Stream data as well -- or else it wouldn't be an exact copy.

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:

static Bitmap LoadImage(Stream stream)
{
    Bitmap retval = null;

    using (Bitmap b = new Bitmap(stream))
    {
        retval = new Bitmap(b.Width, b.Height, b.PixelFormat);
        using (Graphics g = Graphics.FromImage(retval))
        {
            g.DrawImage(b, Point.Empty);
            g.Flush();
        }
    }

    return retval;
}

And then you can invoke it like such:

using (Stream s = ...)
{
    Bitmap x = LoadImage(s);
}