Free file locked by new Bitmap(filePath)
Solution 1
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.
Solution 2
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);
Solution 3
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.
Solution 4
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);
}
Solution 5
As far as I know, this is 100% safe, since the resulting image is 100% created in memory, without any linked resources, and with no open streams left behind in memory. It acts like any other Bitmap
that's created from a constructor that doesn't specify any input sources, and unlike some of the other answers here, it preserves the original pixel format, meaning it can be used on indexed formats.
Based on this answer, but with extra fixes and without external library import.
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
public static Bitmap CloneImage(Bitmap sourceImage)
{
Rectangle rect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
Bitmap targetImage = new Bitmap(rect.Width, rect.Height, sourceImage.PixelFormat);
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
BitmapData sourceData = sourceImage.LockBits(rect, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
BitmapData targetData = targetImage.LockBits(rect, ImageLockMode.WriteOnly, targetImage.PixelFormat);
Int32 actualDataWidth = ((Image.GetPixelFormatSize(sourceImage.PixelFormat) * rect.Width) + 7) / 8;
Int32 h = sourceImage.Height;
Int32 origStride = sourceData.Stride;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
Int32 targetStride = targetData.Stride;
Byte[] imageData = new Byte[actualDataWidth];
IntPtr sourcePos = sourceData.Scan0;
IntPtr destPos = targetData.Scan0;
// Copy line by line, skipping by stride but copying actual data width
for (Int32 y = 0; y < h; y++)
{
Marshal.Copy(sourcePos, imageData, 0, actualDataWidth);
Marshal.Copy(imageData, 0, destPos, actualDataWidth);
sourcePos = new IntPtr(sourcePos.ToInt64() + origStride);
destPos = new IntPtr(destPos.ToInt64() + targetStride);
}
targetImage.UnlockBits(targetData);
sourceImage.UnlockBits(sourceData);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// For indexed images, restore the palette. This is not linking to a referenced
// object in the original image; the getter of Palette creates a new object when called.
if ((sourceImage.PixelFormat & PixelFormat.Indexed) != 0)
targetImage.Palette = sourceImage.Palette;
// Restore DPI settings
targetImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
return targetImage;
}
To call, simply use:
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
Or, from bytes:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
Related videos on Youtube
MrCatacroquer
Updated on August 11, 2020Comments
-
MrCatacroquer almost 4 years
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?
-
Lilith River about 13 yearsYou have to clone the bitmap - Bitmaps hold the stream open, or else throw exceptions. See msdn.microsoft.com/en-us/library/z7ha67kw.aspx
-
BrainSlugs83 over 12 yearsNO! Bad! "You must keep the stream open for the lifetime of the Bitmap." -- MSDN. As posted below by @Computer Linguist.
-
Sam Saffron over 12 years@BrainSlugs83 your comment is correct, however we can not control what our users do, you should downvote incorrect answer if you feel they are wrong or actively harmful
-
dbort over 12 years@BrainSlugs83 Updated the answer with a technique I found elsewhere to get around the bitmap lifetime.
-
RenniePet about 11 yearsThere's a minor problem with this in that the pixel format will always be 32-bit ARGB and the resolution will always be 96 dpi. This may be OK for most applications, but for some applications it's important to try to maintain the pixel format and resolution of the source file.
-
Bitterblue over 10 yearsThis answers still fails.
-
stricq about 10 yearsYou still have to keep the stream open for the life of the image, which means the data is in memory twice. This is impracticable for larger images.
-
JochemKempe about 9 yearsThis doesn't work, it changes the PixelFormat and maybe other things
-
ChrisW almost 9 yearsIn your code example it's not clear whether/when you should (and/or do) call Dispose on the MemoryStream and/or on the Image. Peraps I shouldn't complain however that (i.e. calling Dispose) is part of the essence of this question.
-
Nikwin almost 9 yearsThe image uses the stream after it is created (sorry, I don't recall the scenarios where it does). If you dispose of it, it will throw an exception. It's not actually important to call Dispose on a MemoryStream since it doesn't use any system resources (such as a file lock when using a FileStream). The GC will clean it up properly when it is time.
-
J3soon almost 9 yearsThanks, this helped me a lot.
-
ToolmakerSteve over 7 yearsDrawImage into a Bitmap with matching characteristics seems the simplest way to transfer the Bitmap, without losing pixel format. To preserve dpi, see my comment on that answer.
-
ToolmakerSteve over 7 yearsTo preserve dpi, after
retval = ...;
add:retval.SetResolution(b.HorizontalResolution, b.VerticalResolution);
-
ToolmakerSteve over 7 yearsDUBIOUS. As mentioned in BrainSlugs answer, and verified by Anlo, "Clone" does not help the situation. Other answers are safer.
-
ToolmakerSteve almost 7 years@NathanaelJones - to be precise, must
Copy
the bitmap (see other answers for various ways to do so), notClone
; a clone contains all the same fields, including the stream reference that causes the exception. -
ToolmakerSteve almost 7 years
-
ToolmakerSteve almost 7 years... also net prog's answer is another safe way to copy. Note that all of these in-memory approaches double memory consumption compared to opening from a file (the bitmap itself uses memory, and those techniques also hold data in a memory stream or array that acts as a backing store in case the bitmap needs to be recreated); for a very large file, the technique of copying to a temporary file is superior, as the file acts as the persistent backing store.
-
Martin Davies over 6 yearsThis should be the accepted answer in my opinion. This has helped me immensely in a legacy product with recurring file access issues, thank you for this. So simple when you see it...!
-
Nyerguds over 6 years@ToolmakerSteve This does not work for indexed formats, though. The only way to do it for those is using
LockBits
to copy of the backing array, and then restore the palette. -
Nyerguds over 6 yearsDo note, I have no clue how this would need to be done in case of an animated gif...
-
RenniePet over 6 years@Nyerguds I would assume so, but I don't know for sure. Why don't you try it?
-
Nyerguds over 6 yearsIt works... but, I checked the code behind this, and it seems that internally, all it does is make a byte stream and call
Image.FromStream()
on it, with a not-very-reassuring note in the reference MSDN code note that "hopefully GDI+ knows what to do with this!" So like the other solutions, this leaves an unclosed stream somewhere in memory. -
RenniePet over 6 years@Nyerguds What you say may be true, but it is an unclosed (or undiscarded) memory stream, not an open file stream and a locked file. And I'm thinking that garbage collection will dispose it and recover the memory when the Image object is no longer referenced anywhere in your program.
-
Nyerguds over 6 yearsOh, it works. I just don't find it a very clean solution.
-
ToolmakerSteve over 6 yearsFYI, also see Brian's answer, which avoids creating bitmap twice, so less overhead; instead it uses a byte array as the intermediate to "break" the dependency on the file.
-
Kasey Speakman about 6 yearsThis is broken for me on non-index images. When trying to Marshal.Copy the last row, get an AccessViolationException. I guess it is reading past the end of the source array.
-
Nyerguds about 6 yearsThe calculations are correct, and I've been using this for ages without any problems, for both indexed and non-indexed images. You sure you didn't do something else wrong, like closing a stream after loading an image from it?
-
Nyerguds about 6 years@KaseySpeakman FYI: An
Image
object created from a stream will need the stream to remain open for the entire life cycle of the image object. Unlike with files, there is nothing actively enforcing this, but after the stream is closed, the image will give errors when saved, cloned or otherwise manipulated. -
Kasey Speakman about 6 yearsAh, I thought the purpose was to disconnect the image from the backing resource like the stream by creating a new image. So I am closing the source image and its stream after cloning to the target image.
-
Nyerguds about 6 yearsYeah, but the disconnected one is the newly created image; the image that is fed into this function still needs to be valid during this operation. The usage examples at the bottom show how it should be used.
-
Kasey Speakman about 6 yearsI had pretty much copied the code as-is, so not sure. Oh well, I got it working a different way (
Graphics.DrawImage
). -
elle0087 almost 6 yearsif you need a Bitmap you can do : return (Bitmap)Image.FromStream(ms);
-
JimmyUK1 over 5 years@Brian Do you know if is necessary to call Dispose on an Image based on a MemoryStream then? Does Image hold on to any possibly unmanaged resources besides the stream?
-
Nikwin over 5 years@asgerhallas You will definitely want to call Dispose on the image. It holds native GDI objects that must be destroyed.
-
Ctrl S over 5 yearsI couldn't open a new
Form
due to an image being opened at runtime from the same directory. This answer solved the issue. -
Nyerguds almost 5 years@elle0087
Image.FromFile()
andImage.FromStream()
really just call the Bitmap constructor and then lose the specificBitmap
type in their return value. It's cleaner to just callnew Bitmap(...)
directly. -
Nyerguds over 4 yearsThis will crash on indexed images, because the
Graphics
class can't handle them. -
Tobias Knauss almost 3 years@Nyerguds: Thanks, that saved me some time. In combination with the hint from @RenniePet, it's a good solution: 1) Load Bitmap #1 from file. 2) Create new Bitmap #2 with same size and pixel format, set resolution, set palette. 3) LockBits on both bitmaps,
int copySize = bitmapData1.Stride * bitmapData1.Height
Buffer.BlockCopy(bitmapData1.ToPointer(), bitmapData2.ToPointer(), copySize, copySize)
from first to second bitmap (this will requireunsafe
on the method, but you can work around it using 2xMarshal.Copy()
.