Archivo libre bloqueado por un nuevo bitmap (filePath)

Tengo la imagen de un PictureBox apuntando a un cierto archivo “A”, en el tiempo de ejecución quiero cambiar la imagen del PictureBox a una “B” diferente, pero me sale el siguiente error:

“Se produjo una excepción de primera oportunidad del tipo ‘System.IO.IOException’ en mscorlib.dll Información adicional: el proceso no puede acceder al archivo” A “porque lo está utilizando otro proceso.”

Estoy configurando la imagen de la siguiente manera:

pbAvatar.Image = new Bitmap(filePath); 

¿Cómo puedo desbloquear el primer archivo?

¡Gracias por adelantado!

El uso de una secuencia de archivos desbloqueará el archivo una vez que se haya leído y desechado:

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

Editar: se actualizó para permitir que se elimine el bitmap original y permite que FileStream se cierre.

ESTA RESPUESTA NO ES SEGURA – Ver comentarios y ver la discusión en la respuesta de net_prog . Editar para usar Clone no lo hace más seguro: Clone clona todos los campos, incluida la referencia de la ruta de archivos, que en ciertas circunstancias causará un problema.

Aquí está mi enfoque para abrir una imagen sin bloquear el archivo …

 public static Image FromFile(string path) { var bytes = File.ReadAllBytes(path); var ms = new MemoryStream(bytes); var img = Image.FromStream(ms); return img; } 

ACTUALIZACIÓN: Hice algunas pruebas de rendimiento para ver qué método era el más rápido. Lo comparé con la respuesta @net_progs “copiar desde bitmap” (que parece ser la más cercana para corregir, aunque tiene algunos problemas). Cargué la imagen 10000 veces para cada método y calculé el tiempo promedio por imagen. Aquí están los resultados:

 Loading from bytes: ~0.26 ms per image. Copying from bitmap: ~0.50 ms per image. 

Los resultados parecen tener sentido ya que tiene que crear la imagen dos veces usando la copia del método de bitmap.

Esta es una pregunta de locking común ampliamente debatida en la web.

El truco sugerido con la transmisión no funcionará , en realidad funciona inicialmente, pero causa problemas más adelante. Por ejemplo, cargará la imagen y el archivo permanecerá desbloqueado, pero si intenta guardar la imagen cargada mediante el método Save (), generará una excepción GDI + genérica.

A continuación, el camino con la replicación por píxel no parece ser sólido, al menos es ruidoso.

Lo que encontré trabajando se describe aquí: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile–locks-file.aspx

Así es como debe cargarse la imagen:

 Image img; using (var bmpTemp = new Bitmap("image_file_path")) { img = new Bitmap(bmpTemp); } 

Estaba buscando una solución a este problema y este método funciona bien para mí hasta ahora, así que decidí describirlo, ya que descubrí que muchas personas aconsejan el enfoque de flujo incorrecto aquí y en la web.

No puede deshacer / cerrar una secuencia mientras un objeto de bitmap todavía la está usando. (Si el objeto de bitmap necesitará acceder de nuevo solo es determinista si sabe con qué tipo de archivo está trabajando y exactamente qué operaciones va a realizar. Por ejemplo, para ALGUNAS imágenes en formato .gif, la transmisión se cierra antes el constructor regresa)

Clone crea una “copia exacta” del bitmap (por documentación; ILSpy lo muestra llamando a métodos nativos, por lo que es demasiado para rastrear en este momento), también copia esos datos de Stream, o de lo contrario no sería un copia exacta.

Su mejor opción es crear una réplica perfecta de píxeles de la imagen, aunque YMMV (con ciertos tipos de imágenes puede haber más de un cuadro, o puede que tenga que copiar datos de paleta también). Pero para la mayoría de las imágenes, esto funciona :

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

Y luego puedes invocarlo así:

 using (Stream s = ...) { Bitmap x = LoadImage(s); } 

Aquí está la técnica que estoy usando actualmente, y parece funcionar mejor. Tiene la ventaja de producir un objeto Bitmap con el mismo formato de píxel (24 bits o 32 bits) y resolución (72 ppp, 96 ppp, lo que sea) como archivo de origen.

  // ImageConverter object used to convert JPEG byte arrays into Image objects. This is static // and only gets instantiated once. private static readonly ImageConverter _imageConverter = new ImageConverter(); 

Esto puede usarse tantas veces como sea necesario, de la siguiente manera:

  Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName)); 

Editar: Aquí hay una actualización de la técnica anterior: https://stackoverflow.com/a/16576471/253938

( La respuesta aceptada es incorrecta. Cuando intente LockBits(...) en el bitmap clonado, eventualmente encontrará errores GDI +).


Solo veo 3 formas de salir de esto:

  • Copie su archivo a un archivo temporal y ábralo de la manera más fácil new Bitmap(temp_filename)
  • abra su archivo, lea la imagen, cree una copia de formato pixel-size-pixel (no Clone() ) y elimine el primer bitmap
  • (acepta la característica de archivo bloqueado)

Hasta donde yo sé, esto es 100% seguro, ya que la imagen resultante se crea al 100% en la memoria, sin ningún recurso vinculado, y sin streams abiertas en la memoria. Actúa como cualquier otro Bitmap creado a partir de un constructor que no especifica ninguna fuente de entrada y, a diferencia de otras respuestas aquí, conserva el formato de píxeles original, lo que significa que puede usarse en formatos indexados.

En función de esta respuesta , pero con correcciones adicionales y sin importar la biblioteca externa.

 ///  /// Clones an image object to free it from any backing resources. /// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes. ///  /// The image to clone /// The cloned image 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; 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); // 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; } 

Para llamar, simplemente use:

 /// Loads an image without locking the underlying file. /// Path of the image to load /// The image public static Bitmap LoadImageSafe(String path) { using (Bitmap sourceImage = new Bitmap(path)) { return CloneImage(sourceImage); } } 

O bien, desde bytes:

 /// Loads an image from bytes without leaving open a MemoryStream. /// Byte array containing the image to load. /// The image public static Bitmap LoadImageSafe(Byte[] fileData) { using (MemoryStream stream = new MemoryStream(fileData)) using (Bitmap sourceImage = new Bitmap(stream)) { { return CloneImage(sourceImage); } } 

Léelo en la secuencia, crea un bitmap y cierra la transmisión.

Hace tres años, escribí un progtwig de visor de imágenes solo para ver si podía hacerlo. La semana pasada añadí código para escanear imágenes. (Planeo agregar esto a un progtwig de genealogía después de que saque los errores). Para recortar el área no utilizada, haré que el progtwig llame a MSPaint con el nombre del archivo. Lo edito allí y luego lo guardo. Cuando cierro Paint, la imagen muestra los cambios.
Estaba obteniendo un error en Paint sobre el locking del archivo si hacía algo con la imagen. Cambio el progtwig para bloquear una imagen, usando Image, FromStream (). Ya no recibo ese mensaje en Paint. (Mi progtwig está en VB 2010.)