UPDATED
I used below solutions (loading Image from stream), but get new problem. img object is absolutely correct Image class instance with all field filled with correct values. But calling
img.Save("path/to/new/image.bmp");
on it results in new exception for GDI+ (System.Runtime.InteropServices.ExternalException, in GDI+ interface) - I get error message but is in polish, am not sure how to translate it.
Original question
I have problem with C# .NET Framework 2.0
Basically I'am trying to achieve:
Image img = Image.FromFile("Path/To/Image.bmp");
File.Delete("Path/To/Image.bmp"); // Exception, the file is in use!
It is important for me to keep copy of image in memory when original file was deleted. I though it is somehow odd that .NET still lock file on hard disc despite it is no longer required for any operation (entire image is now in memory, isn't it?)
So I tried this solution:
Image img = new Image(Image.FromFile("Path/To/Image.bmp")); // Make a copy
// this should immiedietaly destroy original loaded image
File.Delete("Path/To/Image.bmp"); // Still exception: the file is in use!
I can do:
Image img = null;
using(Image imgTmp = Image.FromFile("Path/To/Image.bmp"))
{
img = new Bitmap(imgTmp.Width, imgTmp.Height, imgTmp.PixelFormat);
Graphics gdi = Graphics.FromIage(img);
gdi.DrawImageUnscaled(imgTmp, 0, 0);
gdi.Dispose();
imgTmp.Dispose(); // just to make sure
}
File.Delete("Path/To/Image.bmp"); // Works fine
// So I have img!
But this seems to me almost like using nuke to kill bug... and raises another problem: GDI poorly support Drawing palette-based images onto each other (and palette ones are majority in my collection).
Am I doing something wrong? Is any better way to have Image in memory and original file deleted from hard disk?
This should do the trick:
Image img = null;
using (var stream = File.OpenRead(path)) {
img = Image.FromStream(stream);
}
File.Delete(path);
UPDATE: Don't use the code above!
I've found the related knowledge base article: http://support.microsoft.com/?id=814675
The solution is to really copy the bitmap as outlined in the article. I've coded the two ways that the article mentions (the first one was the one you were doing, the second one is the one in your answer, but without using unsafe
):
public static Image CreateNonIndexedImage(string path) {
using (var sourceImage = Image.FromFile(path)) {
var targetImage = new Bitmap(sourceImage.Width, sourceImage.Height,
PixelFormat.Format32bppArgb);
using (var canvas = Graphics.FromImage(targetImage)) {
canvas.DrawImageUnscaled(sourceImage, 0, 0);
}
return targetImage;
}
}
[DllImport("Kernel32.dll", EntryPoint = "CopyMemory")]
private extern static void CopyMemory(IntPtr dest, IntPtr src, uint length);
public static Image CreateIndexedImage(string path) {
using (var sourceImage = (Bitmap)Image.FromFile(path)) {
var targetImage = new Bitmap(sourceImage.Width, sourceImage.Height,
sourceImage.PixelFormat);
var sourceData = sourceImage.LockBits(
new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
ImageLockMode.ReadOnly, sourceImage.PixelFormat);
var targetData = targetImage.LockBits(
new Rectangle(0, 0, sourceImage.Width, sourceImage.Height),
ImageLockMode.WriteOnly, targetImage.PixelFormat);
CopyMemory(targetData.Scan0, sourceData.Scan0,
(uint)sourceData.Stride * (uint)sourceData.Height);
sourceImage.UnlockBits(sourceData);
targetImage.UnlockBits(targetData);
targetImage.Palette = sourceImage.Palette;
return targetImage;
}
}