File.Delete falla cuando se llamó a Image.FromFile antes, a pesar de hacer una copia de la imagen cargada y destruir una original

ACTUALIZADO

Utilicé las siguientes soluciones (cargando Imagen de la transmisión), pero aparece un problema nuevo. El objeto img es absolutamente correcto. Instancia de la clase de imagen con todos los campos rellenos con los valores correctos. Pero llamando

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

en él resulta una nueva excepción para GDI + (System.Runtime.InteropServices.ExternalException, en la interfaz GDI +) – Obtengo un mensaje de error pero está en polaco, no estoy seguro de cómo traducirlo.

Pregunta original

Tengo un problema con C # .NET Framework 2.0

Básicamente, estoy tratando de lograr:

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

Es importante para mí mantener copia de la imagen en la memoria cuando se eliminó el archivo original. Creo que es algo extraño que .NET todavía bloquee el archivo en el disco duro a pesar de que ya no es necesario para ninguna operación (toda la imagen está ahora en la memoria, ¿no?)

Así que probé esta solución:

 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! 

Puedo hacer:

 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! 

Pero esto me parece casi como utilizar nuke para matar errores … y plantea otro problema: GDI no es compatible con el dibujo de imágenes basadas en paletas (y las de paleta son mayoría en mi colección).

¿Estoy haciendo algo mal? ¿Hay alguna forma mejor de eliminar Image Image in memory y el archivo original del disco duro?

Esto debería funcionar:

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

ACTUALIZACIÓN : ¡No use el código de arriba!

Encontré el artículo relacionado de la base de conocimiento: http://support.microsoft.com/?id=814675

La solución es copiar realmente el bitmap como se describe en el artículo. He codificado las dos formas en que el artículo menciona (el primero fue el que estaba haciendo, el segundo es el que está en su respuesta, pero sin usar 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; } } 

Su problema es que la nueva imagen aún sabe de dónde vino, al haber recibido el identificador del antiguo constructor de copias de la imagen, por lo que el tiempo de ejecución aún sabe que tiene un identificador abierto para el archivo.

Es posible que pueda evitar este comportamiento con un Stream en su lugar:

 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. 

Aquí hay una versión más limpia con una cláusula de 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 

Caveat Emptor; No lo he probado, no hay garantía de que resuelva el problema, pero parece razonable. Trabajar directamente con Stream le proporciona, y no la implementación de la imagen, control sobre el manejo del archivo, para que pueda asegurarse de que su progtwig libere recursos cuando USTED lo desee.

sólo hay que poner

GC.Collect();

al final debería funcionar bien

Esto funciona bien, la desventaja es que requiere una comstackción “insegura”.

La versión en la que se carga la imagen de la secuencia que se cancela cuando se realiza la carga no permite guardar la imagen en el disco a través del clásico 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; } 

como compartir de otra manera

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