投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/6/5 14:41:42
> クローンでないときに、クローズするとエラーになるならわかるのですが
> 結果は逆なのです
今回のケースでは、Clone した Image もまた、同じストリームへの参照を
有していたのだと考えてみると、わかりやすいかと思います。


たとえば下記の場合、Clone した bmp2 側を操作していますが、
fs.Dispose() していなければ、第5フレームに切り替わるのに対し、
fs.Dispose() していると、SelectActiveFrame の呼び出しで
ExternalException (GDI+ で汎用エラーが発生しました。) が飛んできます。


'複数解像度のマルチページ TIFF (1,971,467 バイト) 
' http://merovingio.c2rmf.cnrs.fr/iipimage/PalaisDuLouvre.tif 
Dim imgFile As String = "C:\temp\PalaisDuLouvre.tif"

Dim fs As New FileStream(imgFile, FileMode.Open)
Dim bmp1 As New Bitmap(fs)

'フレームを第3ページに切り替える 
bmp1.SelectActiveFrame(FrameDimension.Page, 3)

'クローンして bmp2 を生成 
Dim bmp2 As Bitmap = DirectCast(bmp1.Clone(), Bitmap)

fs.Dispose()
bmp2.SelectActiveFrame(FrameDimension.Page, 5)




> 分割画面(i) = マルチ画像.Clone
SelectActiveFrame メソッドを使っていますので、
「分割画面」というよりは「選択画面」かも知れません。

マルチ画像.GetFrameCount(FrameDimension.Page) > 1 だった場合、
分割画面(i).GetFrameCount(FrameDimension.Page) > 1 のままになるので、
これを『分割』と呼ぶのは、少し違和感がありました。

それぞれが単一フレームとなるよう写し取れば、「分割」っぽくなるかもしれませんね。
下記のコードだと、色数等が変化してしまいますが…。

Dim images As New List(Of Image)()
For idx As Integer = 0 To frameCount - 1
    srcImage.SelectActiveFrame(fd, idx)
    Dim bmp As New Bitmap(srcImage.Width, srcImage.Height)
    Using g = Graphics.FromImage(bmp)
        g.DrawImage(srcImage, 0, 0)
    End Using
    images.Add(bmp)
Next
Return images.ToArray()



> closeしないというのはわかるのですが、それでも不思議です
では、提示頂いた実験コードを順にみてみましょうか。
それぞれ上から A, B, C パターンと呼ぶことにします。
元画像となる「マルチ画像」は、3 つのページを持つ画像だとします。


まず最初は、エラーにならなかっという B のコードから見てみます。

> マルチ画像 = System.Drawing.Image.FromStream(ストリーム)
> For i = 0 To ページ数 - 1
>   マルチ画像.SelectActiveFrame(fd, i)
>   分割画面(i) = マルチ画像
> Next
この場合、
 マルチ画像 Is 分割画面(0) は True
 マルチ画像 Is 分割画面(1) は True
 マルチ画像 Is 分割画面(2) は True
の状態となります。全部同じインスタンスですね。

> ストリーム.Close()         エラーにならない
全ページを列挙選択したため、イメージ情報の一部がキャッシュされたことで、
偶然エラーにならなかったということではないでしょうか。
リソースからの追加読み込みが必要になった時点で、
やはりエラーになってしまう可能性があります。


続いて、A および C のコードです。
上記と同じように、まずはループ部分までを見てみます。

> マルチ画像 = System.Drawing.Image.FromStream(ストリーム)
> For i = 0 To ページ数 - 1
>   マルチ画像.SelectActiveFrame(fd, i)
>   分割画面(i) = マルチ画像.Clone
> Next
このループが終了した時点で、
 マルチ画像:「選択ページ番号:2」「参照リソース:開いているストリーム」
 分割画面(0):「選択ページ番号:0」「参照リソース:開いているストリーム」
 分割画面(1):「選択ページ番号:1」「参照リソース:開いているストリーム」
 分割画面(2):「選択ページ番号:2」「参照リソース:開いているストリーム」
の状態です。

この時点までには、Dispose も Close も行われていないので、何の問題もありません。

コード C では上記で終わりですが、コード A ではさらに下記のコードが実行されています。
> ストリーム.Close()    'これはエラーになる
これにより、
 マルチ画像:「選択ページ番号:2」「参照リソース:破棄されたストリーム」
 分割画面(0):「選択ページ番号:0」「参照リソース:破棄されたストリーム」
 分割画面(1):「選択ページ番号:1」「参照リソース:破棄されたストリーム」
 分割画面(2):「選択ページ番号:2」「参照リソース:破棄されたストリーム」
となります。
そのため、未キャッシュな情報を参照リソースからロードしようとしたタイミングで、
内部エラーを誘発する可能性があるかと思います。