File.Delete在之前调用Image.FromFile时失败,尽管复制了加载的图像并破坏了原始图像

更新

我使用下面的解决方案(从流加载图像),但得到新的问题。 img对象是绝对正确的Image类实例,所有字段都填充了正确的值。 但是打电话

img.Save("path/to/new/image.bmp"); 

在它上面导致GDI +的新exception(System.Runtime.InteropServices.ExternalException,在GDI +接口中) – 我收到错误消息但是很好,我不知道如何翻译它。

原始问题

我有C#.NET Framework 2.0的问题

基本上我试图实现:

 Image img = Image.FromFile("Path/To/Image.bmp"); File.Delete("Path/To/Image.bmp"); // Exception, the file is in use! 

删除原始文件时,将图像副本保留在内存中非常重要。 我虽然有点奇怪的是.NET仍然锁定硬盘上的文件,尽管它不再需要任何操作(整个图像现在在内存中,不是吗?)

所以我试过这个解决方案:

 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! 

我可以:

 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! 

但在我看来,这几乎就像使用nuke来杀死bug ……并引发了另一个问题:GDI很难支持将基于调色板的图像相互映射(而调色板图像在我的collections中占多数)。

难道我做错了什么? 有没有更好的方法让内存中的图像和原始文件从硬盘中删除?

这应该是诀窍:

  Image img = null; using (var stream = File.OpenRead(path)) { img = Image.FromStream(stream); } File.Delete(path); 

更新 :不要使用上面的代码!

我找到了相关的知识库文章: http : //support.microsoft.com/?id = 814675

解决方案是真正复制文章中概述的位图。 我编写了文章提到的两种方式(第一种是你正在做的那种,第二种是你答案中的那种,但不使用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; } } 

你的问题是新的Image仍然知道它来自哪里,从旧的Image的复制构造函数中获得了文件句柄,因此运行时仍然知道它有一个打开的文件句柄。

您可以使用Stream来解决此问题:

 Image image; FileStream myStream = new FileStream(path); try { image = Image.FromStream(myStream); } finally { myStream.Close(); myStream.Dispose(); } //test that you have a valid Image and then go to work. 

这是一个带有using子句的更干净的版本:

 Image image; using(FileStream myStream = new FileStream(path)) { image = Image.FromStream(myStream); } //a using clause calls Dispose() at the end of the block, //which will call Close() as well 

买者自负; 我没有对此进行测试,也不能保证它能解决问题,但这似乎是合理的。 直接使用Stream可以控制文件句柄,而不是图像实现,因此您可以确保程序在您需要时释放资源。

刚放好

GC.Collect();

最后它应该工作正常

这很好用,缺点是它需要“不安全”的编译。

从加载完成后从流中加载Image时的版本导致无法通过经典GDI +将图像保存到光盘

 public static unsafe Image LoadImageSafe(string path) { Image ret = null; using (Image imgTmp = Image.FromFile(path)) { ret = new Bitmap(imgTmp.Width, imgTmp.Height, imgTmp.PixelFormat); if (imgTmp.PixelFormat == PixelFormat.Format8bppIndexed) { ColorPalette pal = ret.Palette; for (int i = 0; i < imgTmp.Palette.Entries.Length; i++) pal.Entries[i] = Color.FromArgb(imgTmp.Palette.Entries[i].A, imgTmp.Palette.Entries[i].R, imgTmp.Palette.Entries[i].G, imgTmp.Palette.Entries[i].B); ret.Palette = pal; BitmapData bmd = ((Bitmap)ret).LockBits(new Rectangle(0, 0, imgTmp.Width, imgTmp.Height), ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed); BitmapData bmd2 = ((Bitmap)imgTmp).LockBits(new Rectangle(0, 0, imgTmp.Width, imgTmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format8bppIndexed); Byte* pPixel = (Byte*)bmd.Scan0; Byte* pPixel2 = (Byte*)bmd2.Scan0; for (int Y = 0; Y < imgTmp.Height; Y++) { for (int X = 0; X < imgTmp.Width; X++) { pPixel[X] = pPixel2[X]; } pPixel += bmd.Stride; pPixel2 += bmd2.Stride; } ((Bitmap)ret).UnlockBits(bmd); ((Bitmap)imgTmp).UnlockBits(bmd2); } else { Graphics gdi = Graphics.FromImage(ret); gdi.DrawImageUnscaled(imgTmp, 0, 0); gdi.Dispose(); } imgTmp.Dispose(); // just to make sure } return ret; } 

分享其他方式

 try { var img = Image.FromFile(s); var bmp = new Bitmap(img); img.Dispose(); File.Delete(s); } catch { }