none
PaintEventArgs.Dispose メソッドは呼ぶべきか RRS feed

  • 質問

  • System.Windows.Forms.Control クラスを継承したクラスを作り、OnPaint メソッドをオーバーライドしましたが、その引数である PaintEventArgs クラスの Dispose メソッドは呼ぶべきなのでしょうか。

     

    MSDN ライブラリやネット上にある OnPaint メソッドのサンプルコードを見ても、誰も Dispose メソッドは呼んでいないように思えます。

     

    しかし、MSDN ライブラリの PaintEventArgs.Dispose の説明には「PaintEventArgs を使い終わったら、Dispose を呼び出します」と書かれています。

     

    どちらなのでしょうか?

    2008年1月14日 13:59

回答

  • コード ブロック

            private PaintEventArgs _LastInstance;
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                _LastInstance = e;
            }

            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);
                if (_LastInstance != null)
                {
                    Graphics g = _LastInstance.Graphics;                    
                    _LastInstance = null;
                }
            }

     

     

    Graphicsプロパティの参照でOutOfMemoryExceptionが発生しました。

    デバッガで_LastInstance(PaintEventArgs)のプライベートメンバーを参照すると、graphicsがnullになっています。

    このことから、WndProcとOnPaintの間でDisposeされているであろうという動作上の確認はできます。

    (ドキュメントでの裏付けとは違うため、不十分かもしれません)

    2008年1月14日 23:00
    モデレータ
  •  豊田 芳彰 さんからの引用

    ソースがいつ公開されるか、公開されたソースではたして解決するか分かりませんので、この件はとりあえず 「普通に OnPaint をオーバーライドしたり、イベントハンドラで応答してるだけなら、明示的に Dispose を呼ぶ必要はなさそうだ」 とうことで、保留にしておきます。

    年明けに公開されてる予定と聞いていたため「近日」と表現しましたが、米国時間の16日から公開が始まっています。

    Visual Studio 2008(Express Editionを除く)でHotfixを当てて、適切に設定すればベースクラスライブラリのソースコードにステップイン等が行えます。

     

    http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

     

    なお、手元にVisual Studio 2008の正式版がないため、私は試せていません。

    2008年1月17日 14:58
    モデレータ

すべての返信

  •  豊田 芳彰 さんからの引用

    System.Windows.Forms.Control クラスを継承したクラスを作り、OnPaint メソッドをオーバーライドしましたが、その引数である PaintEventArgs クラスの Dispose メソッドは呼ぶべきなのでしょうか。

     

    MSDN ライブラリやネット上にある OnPaint メソッドのサンプルコードを見ても、誰も Dispose メソッドは呼んでいないように思えます。

    OnPaintの外側のWmPaintあたりが処理してくれるのではなかろうかと考えます。

    (確認は取っていません)

     

    PaintEventArgsの説明は、別の目的で使用した際にDisposeが必要という意味だと認識しています。

    2008年1月14日 14:22
    モデレータ
  •  Azulean さんからの引用

    PaintEventArgsの説明は、別の目的で使用した際にDisposeが必要という意味だと認識しています。

     

    「別の目的」 と 「本来の目的」 とはそれぞれ何でしょうか?

    2008年1月14日 15:42
  •  豊田 芳彰 さんからの引用

    「別の目的」 と 「本来の目的」 とはそれぞれ何でしょうか?

    「別の目的」という言い方がまずかったかもしれません。

    .NET Frameworkが用意している既存のイベント(OnPaint, Paint)以外の場所、つまり自らPaintEventArgsのインスタンスを生成した場合には必要かと考えます。

     

    あくまでイメージ

     

    コード ブロック

    public PaintEventHandler CustomPaint = delegate { };

    private void FireCustomPaint()

    {

     using (Graphics g = this.CreateGraphics())

     using (PaintEventArgs e = new PaintEventArgs(g, this.ClientRectangle))

     {

      CustomPaint(this, e);

     }

    }

     

     

    OnPaintのオーバーライドやPaintイベントの有無を問わず、.NET Frameworkの内側ではPaintEventArgsのインスタンスが生成されているので、誰かがDisposeしていないとまずいことになりますし、この辺はOnPaintの外側のメソッドで責任持ってくれるのではないかなと考えています。

    また、派生クラスの派生クラスでbase.OnPaintを先に処理してしまうと、PaintEventArgsが解放されているというのもまずいですし。

     

    そのうち公開されるであろう、.NET Frameworkのソースコードが参照できれば確実だと思います。

    (ILDASMという手も考えられますが、今のところは調べていません)

     

     

    2008年1月14日 22:36
    モデレータ
  • コード ブロック

            private PaintEventArgs _LastInstance;
            protected override void OnPaint(PaintEventArgs e)
            {
                base.OnPaint(e);
                _LastInstance = e;
            }

            protected override void WndProc(ref Message m)
            {
                base.WndProc(ref m);
                if (_LastInstance != null)
                {
                    Graphics g = _LastInstance.Graphics;                    
                    _LastInstance = null;
                }
            }

     

     

    Graphicsプロパティの参照でOutOfMemoryExceptionが発生しました。

    デバッガで_LastInstance(PaintEventArgs)のプライベートメンバーを参照すると、graphicsがnullになっています。

    このことから、WndProcとOnPaintの間でDisposeされているであろうという動作上の確認はできます。

    (ドキュメントでの裏付けとは違うため、不十分かもしれません)

    2008年1月14日 23:00
    モデレータ
  • なるほど、そのように推測する方法もあるですね。参考になります。

     

    正確なところは公開されるソースコードで参照できることを期待するとして、

     

     Azulean さんからの引用

    ILDASMという手も考えられますが

     

    こちらの方法を使って私自身で推測してみたいのですが、ILDASM をどのように使うのでしょうか。

     

    今まで、匿名メソッドなどがどのようなトリックで実現されているのか調べるために ILDASM で逆アセンブルしたことはあるのですが、このツールは PaintEventArgs が Dispose されるかどうか推測する手段にも使えるのでしょうか。

    2008年1月15日 11:14
  •  豊田 芳彰 さんからの引用

    今まで、匿名メソッドなどがどのようなトリックで実現されているのか調べるために ILDASM で逆アセンブルしたことはあるのですが、このツールは PaintEventArgs が Dispose されるかどうか推測する手段にも使えるのでしょうか。

    ずばり答えを見るという意味です。

    しかし、開発者の生成物ではなく、Microsoftの著作物の逆アセンブルにあたるため、EULAとの問題も生じますので辞めた方が無難です。

     

    近日、正式に公開されるソースコードを確認することをお薦めします。

    2008年1月15日 14:17
    モデレータ
  •  Azulean さんからの引用

    ずばり答えを見るという意味です。

     

    あ、ランタイムライブラリそのものを逆アセンブルするということですね。ライセンス的にさすがに、それいいですね、なんて言えるものじゃないですね。

     

    ソースがいつ公開されるか、公開されたソースではたして解決するか分かりませんので、この件はとりあえず 「普通に OnPaint をオーバーライドしたり、イベントハンドラで応答してるだけなら、明示的に Dispose を呼ぶ必要はなさそうだ」 とうことで、保留にしておきます。

     

    ありがとうございました。

    2008年1月16日 12:05
  •  豊田 芳彰 さんからの引用

    ソースがいつ公開されるか、公開されたソースではたして解決するか分かりませんので、この件はとりあえず 「普通に OnPaint をオーバーライドしたり、イベントハンドラで応答してるだけなら、明示的に Dispose を呼ぶ必要はなさそうだ」 とうことで、保留にしておきます。

    年明けに公開されてる予定と聞いていたため「近日」と表現しましたが、米国時間の16日から公開が始まっています。

    Visual Studio 2008(Express Editionを除く)でHotfixを当てて、適切に設定すればベースクラスライブラリのソースコードにステップイン等が行えます。

     

    http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx

     

    なお、手元にVisual Studio 2008の正式版がないため、私は試せていません。

    2008年1月17日 14:58
    モデレータ
  • さっそく System.Windows.Forms.dll や System.Drawing.dll のソースコードを覗いてみたところ、ちゃんと Dispose メソッドが呼ばれていました(PaintEventArgs の Dispose が呼ばれ、その中で Graphics の Dispose が呼ばれていた)。

     

    今回の問題は 「自分で Dispose を呼ぶ必要はない(が、呼んでも問題ない)」 ということで解決しました。

     

    ありがとうございました。

     

    .NET Framework の裏側がこうも簡単に覗けるのは素晴らしいです。興味があるので、他の部分も色々覗いてみます。

    2008年1月19日 4:18