DOBON.NETプログラミング道掲示板

No35322 の記事


■35322 / )  Re[6]: PictureBoxの画像を連続保存
□投稿者/ 魔界の仮面弁士 大御所(1515回)-(2023/01/11(Wed) 17:36:00)
  • アイコン2023/01/11(Wed) 18:08:21 編集(投稿者)

    No35321に返信(ま〜さんの記事)
    > For i As Byte = 0 To 4
    >   Picture(i).Size = New Size(Picture(i).Size.Width, 320)
    >   Picture(i).Size = New Size(Picture(i).Size.Height, 160)
    >   Picture(i).SizeMode = PictureBoxSizeMode.Zoom
    >   Picture(i).Image = Image.FromFile(PicturePath + "MojiGAZou" + CStr(i + 1) + ".bmp")
    >   Picture(i).Dispose()
    > Next

    変数 Picture の宣言が見当たりませんが、
    これは No35320 にある PictureBox 型の一次元配列でしょうか。

    最後に Dispose しているのは何故ですか?


    > Picture(i).Image = Image.FromFile(PicturePath + "MojiGAZou" + CStr(i + 1) + ".bmp")

    VB で文字列連結する場合には、 + 演算子ではなく & 演算子を使う癖をつけた方が良いですよ。

    あるいは上記の処理を、補間文字列構文を使って書くこともできます。
     Picture(i).Image = Image.FromFile($"{PicturePath}MojiGAZou{i + 1}.bmp")
    こちらは、たとえば "MojiGAZou001.bmp" のように 3 桁数字にするために
     Picture(i).Image = Image.FromFile($"{PicturePath}MojiGAZou{i + 1:000}.bmp")
    などと書式指定にも使えるので便利かと。


    なお、画像の読み込みに Image.FromFile を使っておられますが、
    このメソッドは、画像ファイルをロックし続けることになりますのでご注意を。
    状況によっては FromStream を使った方が良いかもしれません。
    https://dobon.net/vb/dotnet/graphics/drawpicture2.html


    No35320に返信(ま〜さんの記事)
    > ただ。PictureBox1も2も.Disposeはしておらず

    流石に、Form 上に貼ってある PictureBox コントロールそのものを
    Dispose したりするとは思っていないのですが、心配していたのは
    「PictureBox の Image プロパティから参照されている最中の画像」を
    誤って Dispose しているのではないか…という点です。

    要は『誰かが使っている最中のオブジェクトを勝手に処分してはいけません』、と。


    > 探した所先のURLの
    > If PictureBox1.Image IsNot Nothing Then
    > PictureBox1.Image.Dispose()
    > End If

    いやいや。元の URL には、その直後に
     「PictureBox1.Image = bmp」
    と書いてあったはずですよ?


    プロパティから参照されているオブジェクトを
    勝手に Dispose するのはとてもマズイことです。

    たとえば下記は、表示中の画像を Dispose する実験コードです。

    Public Class Form1
     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      PictureBox1.Image = Me.Icon.ToBitmap()
     End Sub

     Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
      PictureBox1.Image.Dispose()
     End Sub
    End Class


    ボタンを押しただけでは PictureBox1 の見た目は変わりません。
    しかし、そのあとでフォームを最小化して復元するなどして、
    画像の再描画処理が発生すると、Disposed 状態のオブジェクトにアクセスされることになり、
     『System.ArgumentException: '使用されたパラメーターが有効ではありません。'』
    の例外を誘発する結果になってしまいます。


    割り当てた画像を解放したいのであれば、上記の Button1 の処理を
     Dim oldImage = PictureBox1.Image
     PictureBox1.Image = Nothing
     oldImage?.Dispose()
    もしくは
     Using PictureBox1.Image
      PictureBox1.Image = Nothing
     End Using
    のようにします。これならば、ArgumentException の問題は発生しません。

    あるいはせめて、処分した直後にただちに参照を差し替えるようにして
    Disposed 状態のオブジェクトが参照され続けないようにします。
    No35282 の KOZ さんの実装パターンに相当しますね。

     PictureBox1.Image?.Dispose() '処分
     PictureBox1.Image = Nothing '解放


    なお、この手の Dispose 処理を組み込むにあたっては、
     PictureBox16.Image = bmp
     PictureBox1.Image = bmp
    のような処理が行われていないことが大前提です。
    (同一の画像インスタンスが、複数の PictureBox から参照されているかどうか、ということ)

    上記の場合は、一つの画像が複数の PictureBox から参照されている状態です。
    もしもこの状態で、「bmp.Dispose()」あるいは「PictureBox16.Image.Dispose()」が呼ばれると、
    PictureBox1.Image も当然 Disposed 状態となり、ArgumentException 問題を引き起こします。

    もちろん、
     PictureBox16.Image = otherImage
     PictureBox1.Image = PictureBox1.ErrorImage
     bmp.Dispose()
    などのように、元々参照されていたインスタンスが
    どの PictureBox からも参照されていない状態に差し替えた後ならば、
    Dispose しても安全です。

    もしくは、同一の画像インタンスを共有するのではなく、
     PictureBox1.Image = DirectCast(bmp.Clone(), Image)
    のように、同じ画像の別インスタンスを代入するということもできます。
    これならば、それぞれの PictureBox ごとに、個別に Dipose できます。

    ただし Clone した分だけ、リソース消費が増えてしまう点がデメリットです。

    いずれにせよ、Dispose を呼び出す場合には、そのインスタンスが
    「どこからも使われない状態」を保証してから行うべきである、と言えます。


    > 実際は上記を参考に下記の様に書き開けてます
    > Dim Rbox() As Global.System.Windows.Forms.RichTextBox =
    > {RichTextBox1, RichTextBox2, RichTextBox3, RichTextBox4, RichTextBox5}
    繰り返し使うのであれば、フィールド変数にしておいた方が良いかも。

    Public Class Form1
     Private Rbox As RichTextBox()
     Private Picture As PictureBox ()
     Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
      Rbox = {RichTextBox1, RichTextBox2, RichTextBox3, RichTextBox4, RichTextBox5}
      Picture = {PictureBox1, PictureBox2, PictureBox3, …, PictureBox15}
     End Sub
    End Class
違反を報告
返信 削除キー/


Mode/  Pass/


- Child Tree -