C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Image の使い方について

[トピック内 11 記事 (1 - 11 表示)]  << 0 >>

■89733 / inTopicNo.1)  Image の使い方について
  
□投稿者/ ルパン (3回)-(2018/12/19(Wed) 16:47:14)

分類:[.NET 全般] 


【宣言】
Image[] picimg = new Image[3];

【読込】

picimg[0] = Image.FromFile(filename);

【描画】
Bitmap canvas = new Bitmap(pbxPicture.ClientRectangle.Width, pbxPicture.ClientRectangle.Height);
Graphics g = Graphics.FromImage(canvas);
if (picimg[ipicnum] != null) g.DrawImage(picimg[ipicnum], pbxPicture.ClientRectangle);
g.Dispose();
pbxPicture.Image = canvas;

【保存】
File.Delete(filename);
picimg[ipicnum].Save(filename);

保存の時に
System.IO.IOException:'別のプロセスで使用されているため、プロセスは・・・
と表示されます。

どうすればよいのでしょうか?

どこかにFileStream を使用するといけるようなことが書いてあったので

FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
picimg[0] = Image.FromStream(fs);
fs.Close();

としてみたのですが
OutofMemory が出てしまいました。

引用返信 編集キー/
■89734 / inTopicNo.2)  Re[1]: Image の使い方について
□投稿者/ Hongliang (731回)-(2018/12/19(Wed) 16:54:57)
byte[] bytes = File.ReadAllBytes(path);
MemoryStream stream = new MemoryStream(bytes);
Image img = Image.FromStream(stream);

こんな感じで。
MemoryStreamはDispose(およびClose)する意味もないのでそのままにし、
imgを使い終わった後でimgだけDisposeするようにします。
引用返信 編集キー/
■89735 / inTopicNo.3)  Re[1]: Image の使い方について
□投稿者/ 魔界の仮面弁士 (2000回)-(2018/12/19(Wed) 17:03:01)
No89733 (ルパン さん) に返信
> picimg[0] = Image.FromFile(filename);

BMP, GIF, JPEG, PNG, TIFF の場合、Image.FromFile メソッドは
生成したインスタンスが Dispose されるまで、元のファイルをロックし続けます。

代わりに、
 using(var stm = FileStream(filename, FileMode.Open, FileAccess.Read))
 {
  picimg[0] = Image.FromStream(fs);
 }
もしくは、
 using(var stm = new MemoryStream(File.ReadAllBytes(filename)))
 {
  picimg[0] = Image.FromStream(fs);
 }
を利用してみてください。
引用返信 編集キー/
■89736 / inTopicNo.4)  Re[2]: Image の使い方について
□投稿者/ リンクス (12回)-(2018/12/19(Wed) 17:38:20)
2018/12/19(Wed) 17:40:31 編集(投稿者)

> 代わりに、
>  using(var stm = FileStream(filename, FileMode.Open, FileAccess.Read))
>  {
>   picimg[0] = Image.FromStream(fs);
>  }
> もしくは、
>  using(var stm = new MemoryStream(File.ReadAllBytes(filename)))
>  {
>   picimg[0] = Image.FromStream(fs);
>  }
> を利用してみてください。

こちらは、

 using(var stm = FileStream(filename, FileMode.Open, FileAccess.Read))
 {
  picimg[0] = Image.FromStream(stm);
 }
もしくは、
 using(var stm = new MemoryStream(File.ReadAllBytes(filename)))
 {
  picimg[0] = Image.FromStream(stm);
 }

でしょうか。
引用返信 編集キー/
■89739 / inTopicNo.5)  Re[3]: Image の使い方について
□投稿者/ 魔界の仮面弁士 (2001回)-(2018/12/19(Wed) 18:19:46)
No89736 (リンクス さん) に返信
> picimg[0] = Image.FromStream(stm);
> でしょうか。

そうですね。ご指摘ありがとうございます。

ついでに FileStream 版の方はコンストラクタの呼び出しも間違っていて、

 using (var stm = new FileStream(filename, FileMode.Open, FileAccess.Read))
 {
   picimg[0] = Image.FromStream(stm);
 }

のようにする必要がありました。
テストしていないのがバレバレですね…。



No89734 (Hongliang さん) に返信
> MemoryStreamはDispose(およびClose)する意味もないのでそのままにし、
> imgを使い終わった後でimgだけDisposeするようにします。

ここでは MemoryStream の Dispose を呼んでも呼ばなくても、ほぼ変わりないですね。

MemoryStream を閉じたとしても、ToArray や TryGetBuffer は許容されており、
内部バッファは破棄されない仕様なので。(ただし Read や Write 等は禁止される)

https://pierre3.hatenablog.com/entry/2015/10/25/001207
引用返信 編集キー/
■89740 / inTopicNo.6)  Re[4]: Image の使い方について
□投稿者/ Hongliang (732回)-(2018/12/19(Wed) 18:36:29)
> ここでは MemoryStream の Dispose を呼んでも呼ばなくても、ほぼ変わりないですね。
>
> MemoryStream を閉じたとしても、ToArray や TryGetBuffer は許容されており、
> 内部バッファは破棄されない仕様なので。(ただし Read や Write 等は禁止される)

アニメーションGIF等において、FromStreamの後で、ストリームへのアクセスが発生するケースがあったはずです。
その時にストリームがDisposeされていれば当然例外になるので、ストリームを即座にDisposeしないでいいのならDisposeしない方が安全です。
引用返信 編集キー/
■89747 / inTopicNo.7)  Re[2]: Image の使い方について
□投稿者/ ルパン (4回)-(2018/12/20(Thu) 08:28:25)
 using(var stm = FileStream(filename, FileMode.Open, FileAccess.Read))
 {
  picimg[0] = Image.FromStream(stm);
 }

これを使用すると

「class System.IO.FileStream
同期及び非同期の読み取り操作と書込み操作をサポートするファイル用のStreamを公開します。
実行不可能なメンバー'FileStream'をメソッドのように使用することはできません。」
というエラーになります。
どうしてでしょうか?
引用返信 編集キー/
■89748 / inTopicNo.8)  Re[3]: Image の使い方について
□投稿者/ 魔界の仮面弁士 (2004回)-(2018/12/20(Thu) 09:12:28)
No89747 (ルパン さん) に返信
>  using(var stm = FileStream(filename, FileMode.Open, FileAccess.Read))
> エラーになります。

済みません。これは誤記なので、その後のフォローにもあるように、
No89739 のコードに示した「new」が必要です。


とはいえ実際に使う場合にはこの方法ではなく、
No89734 の Hongliang さんのコードがベストアンサーかと思います。

何故なら No89740 で解説されているように、FromStream メソッドに渡すストリームは
実際には Dispose すべきではない (Image 自体を Dispose すれば良い)ためです。
FileStream を Dispose しないとファイルロックは解除されないので、
今回の要件では FileStream ではなく MemoryStream を使うべきでしょう。


それと、配列の中身を後から差し替えることがある場合は、
 if (picimg[0] != null) { picimg[0].Dispose(); }
 picimg[0] = Image.FromStream(new MemoryStream(File.ReadAllBytes(filename)));
または
 picimg[0]?.Dispose();
 picimg[0] = Image.FromStream(new MemoryStream(File.ReadAllBytes(filename)));
のように、入替前に以前に割り当てていた Image を明示的に Dispose してから
新しいインスタンスに差し替えた方が良いと思います。

ただし上記は、入替前の Image インスタンスが、もはやどこからも使用されていない事が
明確である場合の話です。元の Image インスタンスが、他の場所でも
引き続き使われている場合(たとえば PictureBox に渡しているなど)は
このタイミングで Dispose してはいけません。
引用返信 編集キー/
■89749 / inTopicNo.9)  Re[4]: Image の使い方について
□投稿者/ ルパン (5回)-(2018/12/20(Thu) 09:44:48)
2018/12/20(Thu) 10:03:19 編集(投稿者)
2018/12/20(Thu) 10:01:37 編集(投稿者)
2018/12/20(Thu) 10:00:53 編集(投稿者)

最終的に

picimg[0].Save(filename);
を実行すると

System.Runtime.InteropServices.ExternalException: 'GDI+ で汎用エラーが発生しました。
となります。

同様に
if (System.IO.File.Exists(filename)) File.Delete(filename);
picimg[ipicnum].Save(filename);
ファイルは削除されたのですがSaveで同じエラーが発生します。

テスト的に以下のようなプログラムを使用しています。
for (int ipicnum = 0; ipicnum < picimg.Length; ipicnum++)
{
 if (filename[ipicnum] == "") picimg[ipicnum] = null;
 else
 {
  using (var stm = new FileStream(@filename[ipicnum], FileMode.Open, FileAccess.Read))
  {
   picimg[ipicnum] = Image.FromStream(stm);
  }
 }
}

for (int ipicnum = 0; ipicnum < picimg.Length; ipicnum++)
{
 if (picimg[ipicnum] == null)
 {
  if (File.Exists(filename[ipicnum])) File.Delete(filename[ipicnum]);
 }
 else
 {
  picimg[ipicnum].Save(filename[ipicnum]);
 }
}
引用返信 編集キー/
■89751 / inTopicNo.10)  Re[5]: Image の使い方について
□投稿者/ 魔界の仮面弁士 (2005回)-(2018/12/20(Thu) 10:06:52)
No89749 (ルパン さん) に返信
> using (var stm = new FileStream(@filename[ipicnum], FileMode.Open, FileAccess.Read))
> {
>   picimg[ipicnum] = Image.FromStream(stm);
> }

先の回答を読み直してみてください。
FileStream を使ったこの方法は誤った方法であり、使うべきではありません。
引用返信 編集キー/
■89752 / inTopicNo.11)  Re[6]: Image の使い方について
□投稿者/ ルパン (6回)-(2018/12/20(Thu) 10:40:22)
皆さんありがとうございました。

byte[] bytes = File.ReadAllBytes(path);
が正解だったんですね。
解決済み
引用返信 編集キー/

このトピックをツリーで一括表示


トピック内ページ移動 / << 0 >>

このトピックに書きこむ