File.Delete failing when Image.FromFile was called prior it, despite making copy of loaded image and destroying original one

不羁岁月 提交于 2019-11-27 04:53:28

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; 
  } 
} 

Your problem is that the new Image still knows where it came from, having been given the file handle from the old Image's copy constructor, and so the runtime still knows it has an open handle to the file.

You might be able to work around this behavior with a Stream instead:

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.

Here's a cleaner version with a using clause:

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

Caveat emptor; I have not tested this, no guarantee that it'll solve the problem, but this seems reasonable. Working directly with the Stream gives you, and not the image implementation, control over the file handle, so you can make sure your program releases resources when YOU want.

just put

GC.Collect();

at the end it should work fine

This works fine, the downside is that it requires "unsafe" compilation.

The version when Image is loaded from stream that is killed when loading is done results in unable to save image to disc via classic 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;
}

as share other way

try
{
var img = Image.FromFile(s);
var bmp = new Bitmap(img);
img.Dispose();
File.Delete(s);
}
catch { }
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!