Arquivo livre bloqueado pelo novo Bitmap (filePath)

Eu tenho a imagem de um PictureBox apontando para um determinado arquivo “A”, em tempo de execução eu quero mudar a imagem do PictureBox para um diferente “B”, mas eu recebo o seguinte erro:

“Ocorreu uma exceção de primeira chance do tipo ‘System.IO.IOException’ em mscorlib.dll Informações adicionais: O processo não pode acessar o arquivo” A “porque está sendo usado por outro processo.”

Estou definindo a imagem da seguinte forma:

pbAvatar.Image = new Bitmap(filePath); 

Como posso desbloquear o primeiro arquivo?

Desde já, obrigado!

Usar um stream de vídeo desbloqueará o arquivo depois de lido e descartado:

 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: Atualizado para permitir que o bitmap original seja descartado e permitir que o FileStream seja fechado.

ESTA RESPOSTA NÃO É SEGURA – Veja os comentários e veja a discussão na resposta do net_prog . O Editar para usar Clone não torna mais seguro – Clonar clona todos os campos, incluindo a referência de stream de arquivos, que em certas circunstâncias causará um problema.

Aqui está a minha abordagem para abrir uma imagem sem bloquear o arquivo …

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

ATUALIZAÇÃO: Eu fiz alguns testes de perf para ver qual método era o mais rápido. Eu comparei a @net_progs “copiar de bitmap” resposta (que parece ser o mais próximo para corrigir, embora tenha alguns problemas). Carreguei a imagem 10000 vezes para cada método e calculei o tempo médio por imagem. Aqui estão os resultados:

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

Os resultados parecem fazer sentido, pois é necessário criar a imagem duas vezes usando a cópia do método de bitmap.

Esta é uma questão comum de bloqueio amplamente discutida na web.

O truque sugerido com stream não funcionará , na verdade funciona inicialmente, mas causa problemas mais tarde. Por exemplo, ele carregará a imagem e o arquivo permanecerá desbloqueado, mas se você tentar salvar a imagem carregada pelo método Save (), ela lançará uma exceção GDI + genérica.

Em seguida, o caminho com a replicação por pixel não parece ser sólido, pelo menos é barulhento.

O que eu encontrei trabalhando está descrito aqui: http://www.eggheadcafe.com/microsoft/Csharp/35017279/imagefromfile–locks-file.aspx

É assim que a imagem deve ser carregada:

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

Eu estava procurando uma solução para esse problema e esse método funciona bem para mim até agora, então decidi descrevê-lo, pois descobri que muitas pessoas recomendam a abordagem incorreta do stream aqui e pela web.

Você não pode descartar / fechar um stream enquanto um object de bitmap ainda estiver usando-o. (Se o object de bitmap precisará acessá-lo novamente é apenas determinístico se você souber com que tipo de arquivo você está trabalhando e quais operações você executará. – por exemplo, para algumas imagens em formato .gif, o stream é fechado antes o construtor retorna.

Clone cria uma “cópia exata” do bitmap (por documentação; o ILSpy mostra que ele chama methods nativos, então é muito para rastrear agora), copia esses dados do Stream também – ou então não seria um cópia exata.

Sua melhor aposta é criar uma réplica perfeita de pixel da imagem – embora YMMV (com certos tipos de imagens pode haver mais de um quadro, ou você pode ter que copiar dados de paleta também.) Mas para a maioria das imagens, isso 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; } 

E então você pode invocá-lo assim:

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

Aqui está a técnica que estou usando atualmente e parece funcionar melhor. Ele tem a vantagem de produzir um object Bitmap com o mesmo formato de pixel (24 bits ou 32 bits) e resolução (72 dpi, 96 dpi, qualquer que seja) como o arquivo de origem.

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

Isso pode ser usado sempre que necessário, da seguinte maneira:

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

Edit: Aqui está uma atualização da técnica acima: https://stackoverflow.com/a/16576471/253938

( A resposta aceita está errada. Quando você tenta LockBits(...) no bitmap clonado, eventualmente, você encontrará erros GDI +.)


Eu vejo apenas 3 maneiras de sair disso:

  • copie seu arquivo para um arquivo temporário e abra o caminho fácil new Bitmap(temp_filename)
  • abra seu arquivo, leia a imagem, crie uma cópia de formato pixel-tamanho-pixel (não Clone() ) e descarte o primeiro bitmap
  • (aceite o recurso de arquivo bloqueado)

Até onde eu sei, isso é 100% seguro, já que a imagem resultante é 100% criada na memory, sem resources vinculados e sem streams abertos deixados na memory. Ele age como qualquer outro Bitmap criado a partir de um construtor que não especifica nenhuma fonte de input e, ao contrário de outras respostas, preserva o formato de pixel original, o que significa que pode ser usado em formatos indexados.

Com base nessa resposta , mas com correções extras e sem a importação de bibliotecas externas.

 ///  /// 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 ligar, basta usar:

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

Ou, a partir de 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); } } 

Leia no stream, crie bitmap, feche o stream.

Três anos atrás eu escrevi um programa visualizador de imagens apenas para ver se eu poderia fazê-lo. Na semana passada, adicionei código para digitalizar imagens. (Eu pretendo adicionar isso a um programa de genealogia depois que eu tirar os bugs.) Para cortar área não utilizada, estou tendo o programa chamado MSPaint com o nome do arquivo. Eu edito lá então salve. Quando fecho o Paint, a imagem mostra as alterações.
Eu estava recebendo um erro no Paint sobre o bloqueio do arquivo se eu fiz alguma coisa para a imagem. Eu mudo o programa para bloquear uma imagem, usando Image, FromStream (). Eu não recebo mais essa mensagem no Paint. (Meu programa está no VB 2010.)