none
PictureBoxの内容をClipboardへコピーするには RRS feed

  • 質問

  • PictureBoxの内容をClipboardへコピーしようと、Clipboard.SetDataObjectを利用しようとしていますが、

    Clipboard.SetDataObject(pictureBox1, true);           だと何も起こりませんし、
    Clipboard.SetDataObject(pictureBox1.Image, true);   だと実行時に例外発生が発生します。

    Clipboardへコピーの仕方をお願いします。

    2009年11月10日 8:47

回答

  • >それは設計が悪いです。
    >「暗黙的な方法」が実装されていない場合にリソース漏れが発生しますが、それはクラスライブラリのバグです。

    とは言え、「暗黙的な方法」だけに頼るのはよくありません。

    「暗黙的な方法」が実装されていても、それが効果を発揮するのは、アンマネージリソースをカプセルするオブジェクトがGCによって回収される時になります。

    なので、アンマネージリソースをカプセルするオブジェクトインスタンスがユーザコード内での意味を失ってからGCによってそのインスタンスが回収されるまでの期間、リソースリークと同等の状態が発生します。

    (例えば FileStream のインスタンスを Close() しないでうっちゃらかしておくと、FileStream のインスタンスがGCによって回収するまで、そのファイルの読み書きができない、などの不都合が発生し得ます。)

    # 「その状態をリソースリークと呼ぶのは不適切である」という意見はあるかもしれませんが。

    なので、特別な理由が無い限り、アンマネージリソースをカプセルするオブジェクトインスタンスに対しては、そのオブジェクトインスタンスの寿命をプログラマやフレームワークが明示的に管理するのが基本パターンであるべきと思います。
    • 回答としてマーク クサキ 2009年11月13日 0:29
    2009年11月12日 0:45
    モデレータ
  • 「GCに回収されるまでタイムラグがある」というのは表現ならわかりますし、IDisposableによる解放をした方がいいことも賛成です。しかし元コメントは「解放の対象になりません」と言い切っているので、この点は間違ってると思い指摘しました。
    ざっと読んでみて、それぞれの投稿が「何に対して言及しているか」に迷いました。

    今回の話題の中心となっている、Graphics クラスに対してに限った狭い話であれば、ファイナライザが定義されているためにいずれ回収されるので「解放の対象になりません」は誤りと言えます。

    しかし、自作クラスを含めたクラスデザインという広い話であれば、「きちんと設計しないとリソース漏れを引き起こす」(「解放の対象になりません」)ということが正しいでしょう。
    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に困った第三者の方が検索しやすくなります。
    • 回答としてマーク クサキ 2009年11月13日 0:29
    2009年11月12日 15:14
    モデレータ
  • DOBON.NET さんのコンテンツへのリンクを貼っておきます。

    PictureBoxのImageプロパティに関するよくある勘違い
    http://dobon.net/vb/dotnet/graphics/pictureboximageanddrawimage.html
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク クサキ 2009年11月12日 8:08
    2009年11月10日 14:07
    モデレータ
  • コントロールに直接描画した結果のものは、後に残しておくことはできません。その場限りのものです(DrawToBitmap でハードコピーを取るって乱暴な手段はありますが)。

    後に残しておきたいのなら、コントロールに直接描画するのではなく、新しく Bitmap オブジェクトを作成し、Bitmap に対して(Graphics.FromImage で取得した Graphics を使って)描画を行い、コントロールにはこの Bitmap を描画するだけにしてください(PictureBox なら Paint イベントなど使う必要もなく Image プロパティに設定するだけです。画像に手を加えた後は Invalidate メソッドを呼び出して再描画させます)。

    // 描画手順を残しておいて、PictureBox に描画したのと同じ操作を、新しく作成した Bitmap に対して行うということも可能です。
    • 回答としてマーク クサキ 2009年11月12日 8:08
    2009年11月11日 1:05
  • Hongliang さんの 「PictureBox に描画したのと同じ操作を、新しく作成した Bitmap に対して行う」 方法はこんな感じかな。

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            PaintToImage(g);
        }
    
        private void PaintToImage(Graphics g)
        {
            g.DrawEllipse(new Pen(Color.Green, 3), 20, 20, 40, 40);
        }
    
        private void ClipBoardButton_Click(object sender, EventArgs e)
        {
            using (Bitmap bmp = new Bitmap(pictureBox1 と同じサイズ))
            using (Graphics g = Graphics.FromImage(bmp))
            {
                PaintToImage(g);
                Clipboard.SetDataObject(bmp, true);
            }
        }
    
    なお、Bitmap や Graphics などを自分で作った場合には、使い終わったらきちんと Dispose() してやる必要があります。
    (上記のでは using を使って Dispose() しています)
    もちろん、PaintEventArgs の中にある Graphics だとか、PictureBox.Image の中にある Bitmap だとかは、自分で作ったものではないので勝手に Dispose() してはいけません。
    青柳 臣一 (Shinichi Aoyagi)
    • 回答としてマーク クサキ 2009年11月12日 8:09
    2009年11月11日 2:11
  • これまで、Graphics.FromHwndを使っていましたが、Dispose()を
    書いていませんでしたが、特に問題を起こしていませんでした。
    C#はガベージコネクションが解放するので必要ないと思っていましたが、必要ないのは配列だけということですか?


    .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    マネージリソース(.NET のオブジェクト)は、ガベージコレクタの対象となりますが、
    アンマネージリソース(ファイルやデータベース接続やハンドル等etc・・・)は解放の対象になりません。

    以下、参考URLです。
    http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1220462157

    さらに詳しい資料ですが

    ガベージ・コレクタを明示的に動作させるには?
    http://www.atmarkit.co.jp/fdotnet/dotnettips/021gc/gc.html
    確実な終了処理を行うには?
    http://www.atmarkit.co.jp/fdotnet/dotnettips/027dispose/dispose.html


    以下もっと深い話です。(^ω^;

    ガベージコレクション入門: Microsoft .NET Framework の自動メモリ管理
    http://msdn.microsoft.com/ja-jp/library/bb985010.aspx
    http://msdn.microsoft.com/ja-jp/library/dd297765.aspx

    • 回答としてマーク クサキ 2009年11月12日 8:09
    2009年11月11日 9:13
    モデレータ
  • .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    マネージリソース(.NET のオブジェクト)は、ガベージコレクタの対象となりますが、
    アンマネージリソース(ファイルやデータベース接続やハンドル等etc・・・)は解放の対象になりません。

    それは設計が悪いです。
    アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装 では
    これらのリソースを解放する明示的な方法と暗黙的な方法の両方をサポートする必要があります。暗黙的な制御を提供するには、オブジェクトにプロテクト Finalize を実装します (C# および C++ のデストラクタ構文)。そのオブジェクトへの有効な参照がなくなった後で、ガベージ コレクタがこのメソッドを呼び出します。
    と説明しています。ここで言及されている「暗黙的な方法」が実装されていない場合にリソース漏れが発生しますが、それはクラスライブラリのバグです。
    • 回答としてマーク クサキ 2009年11月13日 0:29
    2009年11月11日 23:49

すべての返信

  • WinXP SP3/VS2008 SP1 で

    Clipboard.SetDataObject(pictureBox1.Image, true); 

    を試してみましたが、正常に実行され、アプリケーション終了後もクリップボードに画像データが存在していることを確認できました。
    ローカルファイル・プロジェクトのリソースと、画像を切り替えて試しましたが、こちらの環境では正常に実行します。

    情報があまりに足りないので、実行環境や、可能なら周辺のソースも提示してもらえませんか。

    • 回答としてマーク クサキ 2009年11月12日 8:10
    • 回答としてマークされていない クサキ 2009年11月12日 8:10
    2009年11月10日 9:25
    モデレータ
  • CreateGraphics だの Paint だので直接 PictureBox に描画してる、PictureBox.Image なんか使ってない、ってパターンかしら。
    2009年11月10日 9:49
  • DOBON.NET さんのコンテンツへのリンクを貼っておきます。

    PictureBoxのImageプロパティに関するよくある勘違い
    http://dobon.net/vb/dotnet/graphics/pictureboximageanddrawimage.html
    解決した場合は、参考になった返信に「回答としてマーク」のボタンを利用して、回答に設定しましょう(複数に設定できます)。
    • 回答としてマーク クサキ 2009年11月12日 8:08
    2009年11月10日 14:07
    モデレータ
  • Vista,  VisualStudio2008 C# Windowsフォームアプリケーションです。

    以下のコーディングです。

            private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                Graphics g = e.Graphics;
                g.DrawEllipse(new Pen(Color.Green, 3), 20, 20, 40, 40);        
            }

            private void ClipBoardButton_Click(object sender, EventArgs e)
            {
                Clipboard.SetDataObject(pictureBox1.Image, true);     // 例外発生          
            }

    エラーは以下のようです。
    'System.ArgumentNullException' のハンドルされていない例外が System.Windows.Forms.dll で発生しました。
    追加情報: 値を Null にすることはできません。

    http://dobon.net/vb/dotnet/graphics/pictureboximageanddrawimage.html

    を見ましたが、具体的にどうして良いか分かりませんのでよろしくお願いします。

     

    2009年11月11日 0:18
  • g が pictureBox1 の Image と結びつけられていません。
    ですから g になにを描いても画面の PictureBox1 にはなにも表示されてませんよね?
    だから、なにも無いのにコピーしようとして ArgumentNullException が発生しています。

    まず PictureBox1 に表示できるようになることです。
    具体例は、すでに教えていただいているリンク先がぴったりだとおもいます。

    2009年11月11日 1:03
  • コントロールに直接描画した結果のものは、後に残しておくことはできません。その場限りのものです(DrawToBitmap でハードコピーを取るって乱暴な手段はありますが)。

    後に残しておきたいのなら、コントロールに直接描画するのではなく、新しく Bitmap オブジェクトを作成し、Bitmap に対して(Graphics.FromImage で取得した Graphics を使って)描画を行い、コントロールにはこの Bitmap を描画するだけにしてください(PictureBox なら Paint イベントなど使う必要もなく Image プロパティに設定するだけです。画像に手を加えた後は Invalidate メソッドを呼び出して再描画させます)。

    // 描画手順を残しておいて、PictureBox に描画したのと同じ操作を、新しく作成した Bitmap に対して行うということも可能です。
    • 回答としてマーク クサキ 2009年11月12日 8:08
    2009年11月11日 1:05
  • Hongliang さんの 「PictureBox に描画したのと同じ操作を、新しく作成した Bitmap に対して行う」 方法はこんな感じかな。

        private void pictureBox1_Paint(object sender, PaintEventArgs e)
        {
            Graphics g = e.Graphics;
            PaintToImage(g);
        }
    
        private void PaintToImage(Graphics g)
        {
            g.DrawEllipse(new Pen(Color.Green, 3), 20, 20, 40, 40);
        }
    
        private void ClipBoardButton_Click(object sender, EventArgs e)
        {
            using (Bitmap bmp = new Bitmap(pictureBox1 と同じサイズ))
            using (Graphics g = Graphics.FromImage(bmp))
            {
                PaintToImage(g);
                Clipboard.SetDataObject(bmp, true);
            }
        }
    
    なお、Bitmap や Graphics などを自分で作った場合には、使い終わったらきちんと Dispose() してやる必要があります。
    (上記のでは using を使って Dispose() しています)
    もちろん、PaintEventArgs の中にある Graphics だとか、PictureBox.Image の中にある Bitmap だとかは、自分で作ったものではないので勝手に Dispose() してはいけません。
    青柳 臣一 (Shinichi Aoyagi)
    • 回答としてマーク クサキ 2009年11月12日 8:09
    2009年11月11日 2:11
  • Bitmapとgを関連付けることが出来るようになり解決しました。
    また、「PictureBox に描画したのと同じ操作を行う」 方法の方がその場だけのメモリの確保でこちらを使うことにしました。
    具体的なコーディングありがとうございました。


    Dispose()に関して追加の質問をさせてください。

    Graphics g = Graphics.FromHwnd(Pic1.Handle);
    Graphics g = Pic1.CreateGraphics();  
    Graphics g = Graphics.FromImage(_bmp);
    これらは、ボタンのClickイベントにあろうが、PictureBoxのPaintイベントにあろうが、
    自分で作った g で、g.Dispose()が必要ということですか?

    これまで、Graphics.FromHwndを使っていましたが、Dispose()を
    書いていませんでしたが、特に問題を起こしていませんでした。
    C#はガベージコネクションが解放するので必要ないと思っていましたが、必要ないのは配列だけということですか?


     

    2009年11月11日 9:05
  • これまで、Graphics.FromHwndを使っていましたが、Dispose()を
    書いていませんでしたが、特に問題を起こしていませんでした。
    C#はガベージコネクションが解放するので必要ないと思っていましたが、必要ないのは配列だけということですか?


    .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    マネージリソース(.NET のオブジェクト)は、ガベージコレクタの対象となりますが、
    アンマネージリソース(ファイルやデータベース接続やハンドル等etc・・・)は解放の対象になりません。

    以下、参考URLです。
    http://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1220462157

    さらに詳しい資料ですが

    ガベージ・コレクタを明示的に動作させるには?
    http://www.atmarkit.co.jp/fdotnet/dotnettips/021gc/gc.html
    確実な終了処理を行うには?
    http://www.atmarkit.co.jp/fdotnet/dotnettips/027dispose/dispose.html


    以下もっと深い話です。(^ω^;

    ガベージコレクション入門: Microsoft .NET Framework の自動メモリ管理
    http://msdn.microsoft.com/ja-jp/library/bb985010.aspx
    http://msdn.microsoft.com/ja-jp/library/dd297765.aspx

    • 回答としてマーク クサキ 2009年11月12日 8:09
    2009年11月11日 9:13
    モデレータ
  • .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    マネージリソース(.NET のオブジェクト)は、ガベージコレクタの対象となりますが、
    アンマネージリソース(ファイルやデータベース接続やハンドル等etc・・・)は解放の対象になりません。

    それは設計が悪いです。
    アンマネージ リソースをクリーンアップするための Finalize および Dispose の実装 では
    これらのリソースを解放する明示的な方法と暗黙的な方法の両方をサポートする必要があります。暗黙的な制御を提供するには、オブジェクトにプロテクト Finalize を実装します (C# および C++ のデストラクタ構文)。そのオブジェクトへの有効な参照がなくなった後で、ガベージ コレクタがこのメソッドを呼び出します。
    と説明しています。ここで言及されている「暗黙的な方法」が実装されていない場合にリソース漏れが発生しますが、それはクラスライブラリのバグです。
    • 回答としてマーク クサキ 2009年11月13日 0:29
    2009年11月11日 23:49
  • >それは設計が悪いです。
    >「暗黙的な方法」が実装されていない場合にリソース漏れが発生しますが、それはクラスライブラリのバグです。

    とは言え、「暗黙的な方法」だけに頼るのはよくありません。

    「暗黙的な方法」が実装されていても、それが効果を発揮するのは、アンマネージリソースをカプセルするオブジェクトがGCによって回収される時になります。

    なので、アンマネージリソースをカプセルするオブジェクトインスタンスがユーザコード内での意味を失ってからGCによってそのインスタンスが回収されるまでの期間、リソースリークと同等の状態が発生します。

    (例えば FileStream のインスタンスを Close() しないでうっちゃらかしておくと、FileStream のインスタンスがGCによって回収するまで、そのファイルの読み書きができない、などの不都合が発生し得ます。)

    # 「その状態をリソースリークと呼ぶのは不適切である」という意見はあるかもしれませんが。

    なので、特別な理由が無い限り、アンマネージリソースをカプセルするオブジェクトインスタンスに対しては、そのオブジェクトインスタンスの寿命をプログラマやフレームワークが明示的に管理するのが基本パターンであるべきと思います。
    • 回答としてマーク クサキ 2009年11月13日 0:29
    2009年11月12日 0:45
    モデレータ
  • 特別な理由が無い限り、アンマネージリソースをカプセルするオブジェクトインスタンスに対しては、そのオブジェクトインスタンスの寿命をプログラマやフレームワークが明示的に管理するのが基本パターンであるべきと思います。

    私も渋木さんの意見に賛成です。

    私も最近まで.NET のガベージコレクションの仕組みをよくわかっておりませんでしたが、
    明示的に解放しない限り GCがいつリソースを解放するか判らないという仕組みを知ってからは

    「いらなくなったら、即 Dipose()」

    を常に心がけるようになりました。
    2009年11月12日 6:40
    モデレータ
  • 「GCに回収されるまでタイムラグがある」というのは表現ならわかりますし、IDisposableによる解放をした方がいいことも賛成です。しかし元コメントは「解放の対象になりません」と言い切っているので、この点は間違ってると思い指摘しました。
    あと、WinFormのControlおよびその派生クラスのように、IDisposableでありながら別フレームワークで解放するものもありますし。そのためDispose()不要ですよね。
    2009年11月12日 12:25
  • 「GCに回収されるまでタイムラグがある」というのは表現ならわかりますし、IDisposableによる解放をした方がいいことも賛成です。しかし元コメントは「解放の対象になりません」と言い切っているので、この点は間違ってると思い指摘しました。
    あと、WinFormのControlおよびその派生クラスのように、IDisposableでありながら別フレームワークで解放するものもありますし。そのためDispose()不要ですよね。
    う~んmmm、言葉尻だけを捕らえて突っ込まれるのも何かと。
    プログラムの設計ミスを全て GC が綺麗に片付けてくれるわけではありませんよね?

    だから

    > .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    と最初に書いているわけなんですが。
    2009年11月12日 13:34
    モデレータ
  • う~んmmm、言葉尻だけを捕らえて突っ込まれるのも何かと。
    プログラムの設計ミスを全て GC が綺麗に片付けてくれるわけではありませんよね?

    だから

    > .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    と最初に書いているわけなんですが。
    別に言葉尻をとらえてのつもりはありません。
    きちんと解放処理を書かないユーザーのためにもクラスライブラリ設計者は暗黙的な解放を実装しなければならない、というのがリンク先の記述です。

    ちなみにひらぽんさんの示したリンク先(Yahoo)の記述も間違ってるし…。Environment.Exit()は解放処理をせずプロセスを終了させます。例えば、usingブロックの中でEnvironment.Exit()を呼び出してもブロックを抜けているわけではないのでDispose()を呼びません。

    私のリンクしたMSDNの記述を読んでいただけたらと思います。
    # それを理解した上で、Dispose()した方がbetterというのが渋木さんのコメントだと思います。
    2009年11月12日 14:27
  • 「GCに回収されるまでタイムラグがある」というのは表現ならわかりますし、IDisposableによる解放をした方がいいことも賛成です。しかし元コメントは「解放の対象になりません」と言い切っているので、この点は間違ってると思い指摘しました。
    ざっと読んでみて、それぞれの投稿が「何に対して言及しているか」に迷いました。

    今回の話題の中心となっている、Graphics クラスに対してに限った狭い話であれば、ファイナライザが定義されているためにいずれ回収されるので「解放の対象になりません」は誤りと言えます。

    しかし、自作クラスを含めたクラスデザインという広い話であれば、「きちんと設計しないとリソース漏れを引き起こす」(「解放の対象になりません」)ということが正しいでしょう。
    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に困った第三者の方が検索しやすくなります。
    • 回答としてマーク クサキ 2009年11月13日 0:29
    2009年11月12日 15:14
    モデレータ
  • 皆様には、私が引き起こした回答に付き合っていただき、誠に感謝いたします。m(_ _;)m
    私としましては、

    Dispose()に関して追加の質問をさせてください。

    ・・・ 中略 ・・・
    これまで、Graphics.FromHwndを使っていましたが、Dispose()を
    書いていませんでしたが、特に問題を起こしていませんでした。
    C#はガベージコネクションが解放するので必要ないと思っていましたが、必要ないのは配列だけということですか?

    に反応し、「Dispose 全般」 に関する質問と捉えて

    > .NET でも、きちんと解放処理を書かないとリソース漏れを起こします。

    つまり・・・

    「使い終わったなら、きちんと Dispose しましょうよ」

    という意味で、回答させて頂いた次第なわけです。言葉足らずで誤解を招いたのなら謝ります。

    2009年11月12日 15:36
    モデレータ
  • >「使い終わったなら、きちんと Dispose しましょうよ」

    ↑は、多くの場合正しいです。

    でも、「IDisposable インターフェースを実装しているオブジェクトインスタンスがプログラマ視点で不要になったら、必ずユーザコード中で  IDisposable.Dispose() 呼び出しを行わなくてはならない」というものでもないんです (^^;

    既に別の例も出てますが、例えば、Control.Paint イベントで渡されてくる Graphics なんかは Windows Forms のフレームワークが寿命を管理しているので、これは、プログラマ視点で「不要」と判断しても、ユーザコードで勝手に Dispose() してはいけません。

    その辺理解はされているんでしょうが、言い切ってしまうと↑のようなパターンをばっさり切り捨てることになるので、物言いがついたものと思います。

    あと、「リソースリーク」という言葉は「アプリケーション終了まで決して解放されるチャンスがない状態」というのが古典的な意味なので、「ファイナライザによって回収されるチャンスが用意されている以上、それはリソースリークではない」というのは正論です。

    ただ、個人的には、オブジェクトインスタンスがユーザコード上での参照を失ってからGCによって回収されるまでの期間に関して注目すれば、ユーザコードもフレームワークも手出しできない状態でオブジェクトインスタンス(と結びつけられたアンマネージリソース)が居座っている(かつ居座ることが他の不都合につながり得る)ので、この状態を「リソースリーク」と呼んでしまっても別に差し支えないかな、と思います。

    この状態を指し示す、簡潔で分かりやすい語があればいいんでしょうけど、「リソースリークのような状態」程度しか思い浮かびません (^^;

    # ちゃんとした用語があるのかな?
    2009年11月13日 1:45
    モデレータ
  • >「使い終わったなら、きちんと Dispose しましょうよ」

    ↑は、多くの場合正しいです。

    でも、「IDisposable インターフェースを実装しているオブジェクトインスタンスがプログラマ視点で不要になったら、必ずユーザコード中で  IDisposable.Dispose() 呼び出しを行わなくてはならない」というものでもないんです (^^;

    既に別の例も出てますが、例えば、Control.Paint イベントで渡されてくる Graphics なんかは Windows Forms のフレームワークが寿命を管理しているので、これは、プログラマ視点で「不要」と判断しても、ユーザコードで勝手に Dispose() してはいけません。

    その辺理解はされているんでしょうが、言い切ってしまうと↑のようなパターンをばっさり切り捨てることになるので、物言いがついたものと思います。


    ありがとうございます。言い切ったのはまずかったですね。(-ω-;

    「リソース漏れを起こす可能性があります」

    程度に留めておけばよかったです。


    また 「暗黙的な方法」 という言葉は知りませんでしたが、私としては

    「Dispose~Finalise パターン」

    という言葉で理解しています。

    「クラス ライブラリ開発のデザインガイドライン」 は一通り目を通しておりましたので、

    「Dispose が呼び出されることを前提にはしないでください。Dispose が呼び出されなかった場合は、型が所有するアンマネージ リソースも Finalize メソッドで解放する必要があります。」

    ということには特に意識しているつもりです。

    実際、現在使っている某ベンダー製の ActiveX が明示的に Dispose しないと怪しい挙動を引き起こすため
    使用クラス側では、Dispose が呼ばれなかった場合を考慮して、Finalize では必ず Dispose を呼ぶ仕様にしております。

    いずれにせよ、GC とアンマネージリソースと .NET のクラスライブラリの関連についていろいろ疑問が生じてきたため、
    別スレッドを立てて質問をさせて頂きたいと思います。

    2009年11月13日 2:30
    モデレータ
  • >また 「暗黙的な方法」 という言葉は知りませんでしたが、私としては
    >「Dispose~Finalise パターン」

    「暗黙的な方法」という用語があるわけではありませんが、Dispose() メソッド呼び出しや using 句などによる明示的な IDisposable.Dispose() メソッド呼び出しに対して、ファイナライザによるユーザコードやフレームワークなどとは関わりなくアンマネージリソースの解放が行われることを指す語であるものと思います。

    >実際、現在使っている某ベンダー製の ActiveX が明示的に Dispose しないと怪しい挙動を引き起こすため

    えーとですね、ActiveX (COM) と Dispose の間には本質的な関係はありません。

    COM は .NET 以前の技術なので、COM オブジェクトインスタンスの寿命は .NET のオブジェクトインスタンスとはまったく異なる仕組みで管理されます。

    .NET から COM オブジェクトのインスタンスを扱う場合、.NET 用のラッパクラスを生成して利用するはずですが、このラッパクラスは IDisposable を実装していなかったはずです。

    なので

    >使用クラス側では、Dispose が呼ばれなかった場合を考慮して、Finalize では必ず Dispose を呼ぶ仕様にしております。

    は、COM オブジェクトインスタンスの寿命管理を明示的に行う、という目的には真正面からは合致していません。

    間接的に COM オブジェクトインスタンスの解放を速める効果がある場合もあるし、そうでない場合も十分考えられます。

     

    2009年11月13日 3:40
    モデレータ
  • > COM は .NET 以前の技術なので、COM オブジェクトインスタンスの寿命は
    > .NET のオブジェクトインスタンスとはまったく異なる仕組みで管理されます。

    それは一応承知しているつもりです。(^ω^;


    > NET から COM オブジェクトのインスタンスを扱う場合、.NET 用のラッパクラスを生成して利用するはずですが、
    > このラッパクラスは IDisposable を実装していなかったはずです。

    いや、それがですね・・・COM のラッパクラスにしっかりあるんです。
    オブジェクトブラウザで追跡したら、ActiveX コントロールなので AxHost クラスがラッピングしていますね。(^ω^;

    でも普通のコントロールのように貼り付れば、
    後はコンテナオブジェクト側で自動的に解放処理が行われるだろうと思っていたら、
    アプリケーションの終了時に COMException が頻発しまくるのでベンダーに尋ねたら、

    「コンテナオブジェクトの終了時に、明示的に Dispose  してください」

    と回答されました。で、結局こんな処理を・・・(-ω-;

    ''' <summary>
    ''' <see>某Control</see> のリソースを解放
    ''' </summary>
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            ' 強制的に COM コントロールを解放
            If (Me.ほげcom1 IsNot Nothing) Then
                Me.ほげcom1.Dispose()
            End If
            If (Me.ほげcom2 IsNot Nothing) Then
                Me.ほげcom2.Dispose()
            End If
            If (Me.ほげcom3 IsNot Nothing) Then
                Me.ほげcom3.Dispose()
            End If
            If (Me.ほげcom4 IsNot Nothing) Then
                Me.ほげcom4.Dispose()
            End If
    
            If (disposing) Then
                If (components IsNot Nothing) Then
                    components.Dispose()
                End If
                GC.SuppressFinalize(Me)
            End If
        Finally
            MyBase.Dispose(disposing)
        End Try
    End Sub
    
    ''' <summary>
    ''' デストラクタ
    ''' </summary>
    Protected Overrides Sub Finalize()
        Dispose(False)
    End Sub
    

    問題の ActiveX は VC6 製で、実は我々は .NET でのファーストユーザーとのことですが。(汗

    ベンダーさんに対しては 「COM は古いから、早く .NET アセンブリで出してくれ」 と催促してますが、
    どうやら 1~2年は確実に無理そうですw

    2009年11月13日 4:08
    モデレータ
  • >> NET から COM オブジェクトのインスタンスを扱う場合、.NET 用のラッパクラスを生成して利用するはずですが、
    >> このラッパクラスは IDisposable を実装していなかったはずです。
    >
    >いや、それがですね・・・COM のラッパクラスにしっかりあるんです。
    >オブジェクトブラウザで追跡したら、ActiveX コントロールなので AxHost クラスがラッピングしていますね。(^ω^;

    ActiveX コントロールだからですかね?
    ずいぶん前に COM オブジェクト(ActiveX DLL)のラッパ見て「ないのかー」とがっかりした記憶があるんですが。

    でも

    >「コンテナクラスの終了時に、明示的に Dispose  してください」

    はちょっと解せないなぁ。

    ActiveX コントロールってことは Form 上に配置するわけで、親 Form (やその配下のコンテナ)に Controls.Add() されてるわけですよね?
    であれば、親 Form が Clsoe() される時に、ActiveX コントロールも Dispose() されると思うんですが。
    2009年11月13日 4:15
    モデレータ
  • でも

    >「コンテナクラスの終了時に、明示的に Dispose  してください」

    はちょっと解せないなぁ。

    ActiveX コントロールってことは Form 上に配置するわけで、親 Form (やその配下のコンテナ)に Controls.Add() されてるわけですよね?
    であれば、親 Form が Clsoe() される時に、ActiveX コントロールも Dispose() されると思うんですが。

    いまベンダーさんとのやり取りを読み返してみたら、COMException ではなく正確には

    System.Runtime.InteropServices.InvalidComObjectException
    「基になる RCW から分割された COM オブジェクトを使うことはできません」

    でした。
    これがアプリ終了時に頻発しまくり、まともに終了できないという問題が発生しておりました。

    ベンダーさんからは 「コンテナオブジェクトの解放時に明示的に Dispose して下さい」 という回答とともに、
    修正版を送っていただいて一応問題は片付きましたが、私も解せない感がありますです。(-ω-;
    2009年11月13日 4:24
    モデレータ
  • あと追記ですが、
    ActiveX コントロールだからですかね?
    ずいぶん前に COM オブジェクト(ActiveX DLL)のラッパ見て「ないのかー」とがっかりした記憶があるんですが。

    ですね。AxHost クラスは System.Windows.Forms.Control を継承してました。

    AxHost クラスの解説には

    「ActiveX コントロールをラップし、完全な機能を備えた Windows フォーム コントロールとしてそのコントロールを公開します。 」

    とありますです。
    2009年11月13日 4:29
    モデレータ
  • 最後に試したのが夏だったので、今後の参考のため、いま一度 「明示的な Dispose 」 を外してデバッグ実行を繰り返してみました。

    ''' <summary>
    ''' <see>某Control</see> のリソースを解放
    ''' </summary>
    Protected Overrides Sub Dispose(ByVal disposing As Boolean)
        Try
            ' コメントアウト!
    ' If (Me.ほげcom1 IsNot Nothing) Then ' Me.ほげcom1.Dispose() ' End If ' If (Me.ほげcom2 IsNot Nothing) Then ' Me.ほげcom2.Dispose() ' End If ' If (Me.ほげcom3 IsNot Nothing) Then ' Me.ほげcom3.Dispose() ' End If ' If (Me.ほげcom4 IsNot Nothing) Then ' Me.ほげcom4.Dispose() ' End If If (disposing) Then If (components IsNot Nothing) Then components.Dispose() End If GC.SuppressFinalize(Me) End If Finally MyBase.Dispose(disposing) End Try End Sub ''' <summary> ''' デストラクタ ''' </summary> Protected Overrides Sub Finalize() Dispose(False) End Sub

    正常に終わる時もあるかと思えば、
    不定期で System.Runtime.InteropServices.InvalidComObjectException が発生したり、
    例外は発生しないもののいつまで立ってもデバッグが停止しなかったり!駄目だこりゃ!! ><

    逆に明示的に Dispose をしている分には問題ありません。それともそう見えるだけ? (謎

    まぁ、そもそも VC6 時代のActiveX を .NET3.5 で動かすのに無理があるのでしょうね。。。(汗


    #かなりスレの本題とは外れてしまいましたね。すみません。m(_ _;)m

    2009年11月13日 5:29
    モデレータ