特定のファイル「A」を指すPictureBoxの画像があります。実行時に、PictureBoxの画像を別の「B」に変更したいのですが、次のエラーが表示されます。
「mscorlib.dllでタイプ 'System.IO.IOException'の最初のチャンス例外が発生しました。追加情報:プロセスはファイル "A"にアクセスできません。別のプロセスによって使用されています。 "
次のように画像を設定しています:
pbAvatar.Image = new Bitmap(filePath);
最初のファイルのロックを解除するにはどうすればよいですか?
ファイルストリームを使用すると、ファイルが読み取られて破棄されると、ファイルのロックが解除されます。
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();
}
編集:元のビットマップを破棄し、FileStreamを閉じることができるように更新されました。
この回答IS NOT SAFE-コメントを参照し、 net_prog's answer の説明を参照してください。 Clone
を使用するための編集では安全性が低下します-クローンは、ファイルストリーム参照を含むすべてのフィールドを複製します。特定の状況では問題が発生します。
ファイルをロックせずに画像を開くための私のアプローチは次のとおりです...
public static Image FromFile(string path)
{
var bytes = File.ReadAllBytes(path);
var ms = new MemoryStream(bytes);
var img = Image.FromStream(ms);
return img;
}
更新:いくつかのパフォーマンステストを行って、どのメソッドが最速であるかを確認しました。これを@net_progsの「ビットマップからコピー」の回答(いくつかの問題はありますが、最も近いと思われます)と比較しました。各メソッドで10000回イメージをロードし、イメージごとの平均時間を計算しました。結果は次のとおりです。
Loading from bytes: ~0.26 ms per image.
Copying from bitmap: ~0.50 ms per image.
ビットマップからのコピー方法を使用してイメージを2回作成する必要があるため、結果は理にかなっているようです。
更新:BitMapが必要な場合は、以下を実行できます。
return (Bitmap)Image.FromStream(ms);
これは、Webで広く議論されている一般的なロックの質問です。
推奨されるストリームトリックは動作しません。実際には最初は動作しますが、後で問題が発生します。たとえば、画像をロードし、ファイルのロックは解除されませんが、Save()メソッドを使用してロードした画像を保存しようとすると、一般的なGDI +例外がスローされます。
次に、ピクセルごとの複製の方法は安定しているようには見えませんが、少なくともうるさいです。
私が働いているとわかったものはここに記述されています: http://www.eggheadcafe.com/Microsoft/Csharp/35017279/imagefromfile--locks-file.aspx
これは、イメージをロードする方法です。
Image img;
using (var bmpTemp = new Bitmap("image_file_path"))
{
img = new Bitmap(bmpTemp);
}
私はこの問題の解決策を探していましたが、これまでのところこの方法はうまく機能しているので、多くの人が間違ったストリームアプローチをこことウェブでアドバイスすることがわかったので、それを説明することにしました。
ビットマップオブジェクトがストリームを使用している間は、ストリームを破棄したり閉じたりすることはできません。 (ビットマップオブジェクトが再びアクセスする必要があるかどうかは、作業しているファイルのタイプと実行する操作を正確に知っている場合にのみ決定的です。たとえば、一部の.gif形式の画像の場合、ストリームは前に閉じられますコンストラクタが戻ります。)
クローンはビットマップの「完全なコピー」を作成します(ドキュメントごと。ILSpyはネイティブメソッドの呼び出しを示しているため、たった今追跡するのは多すぎます)。おそらく、そのStreamデータもコピーします。正確なコピー。
YMMV(特定の種類の画像では複数のフレームが存在する場合や、パレットデータをコピーする必要がある場合もあります)を使用しても、ピクセル完璧な画像のレプリカを作成することをお勧めします:
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;
}
そして、次のように呼び出すことができます:
using (Stream s = ...)
{
Bitmap x = LoadImage(s);
}
ここに私が現在使用しているテクニックがありますが、これが最もうまくいくようです。ソースファイルと同じピクセル形式(24ビットまたは32ビット)および解像度(72 dpi、96 dpiなど)のビットマップオブジェクトを生成できるという利点があります。
// 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();
これは、次のように必要に応じて何度でも使用できます。
Bitmap newBitmap = (Bitmap)_imageConverter.ConvertFrom(File.ReadAllBytes(fileName));
編集:上記の手法の更新は次のとおりです。 https://stackoverflow.com/a/16576471/253938
( 受け入れられた答え は間違っています。クローンされたビットマップでLockBits(...)
を試みると、最終的にGDI +エラーが発生します。)
new Bitmap(temp_filename)
Clone()
しないでください)、最初のビットマップを破棄します私の知る限り、これは100%安全です。なぜなら、結果のイメージはメモリに100%作成され、リンクされたリソースがなく、メモリにオープンストリームが残っていないためです。入力ソースを指定しないコンストラクターから作成される他のBitmap
と同様に機能し、ここでの他の回答の一部とは異なり、元のピクセル形式を保持します。つまり、インデックス付き形式で使用できます。
この回答 に基づいていますが、追加の修正があり、外部ライブラリのインポートはありません。
/// <summary>
/// Clones an image object to free it from any backing resources.
/// Code taken from http://stackoverflow.com/a/3661892/ with some extra fixes.
/// </summary>
/// <param name="sourceImage">The image to clone</param>
/// <returns>The cloned image</returns>
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;
Boolean isFlipped = origStride < 0;
origStride = Math.Abs(origStride); // Fix for negative stride in BMP format.
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);
// Fix for negative stride on BMP format.
if (isFlipped)
targetImage.RotateFlip(RotateFlipType.Rotate180FlipX);
// 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;
}
呼び出すには、次を使用します。
/// <summary>Loads an image without locking the underlying file.</summary>
/// <param name="path">Path of the image to load</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(String path)
{
using (Bitmap sourceImage = new Bitmap(path))
{
return CloneImage(sourceImage);
}
}
または、バイトから:
/// <summary>Loads an image from bytes without leaving open a MemoryStream.</summary>
/// <param name="fileData">Byte array containing the image to load.</param>
/// <returns>The image</returns>
public static Bitmap LoadImageSafe(Byte[] fileData)
{
using (MemoryStream stream = new MemoryStream(fileData))
using (Bitmap sourceImage = new Bitmap(stream)) {
{
return CloneImage(sourceImage);
}
}
ストリームに読み込み、ビットマップを作成し、ストリームを閉じます。