桜、抹茶、白、日記

名古屋市在住のC++使いのcoderの日記だったもの。

IPictureDisp Interface

ネタがないのでCOMの話。

ATLで作ったC++ COM DLLからC#(WPF)にBitmapデータを渡すプログラムを作ったのですが、どうやってBitmapデータを渡すかでとっても悩みました。
最終的にIPictureDispで行く決断をして失敗に終わったのですが、その過程。

まずC++でIPicture/IPictureDispを作成するには、画像ファイルからならOleLoadPictureAPIを、BitmapハンドルからならOleCreatePictureIndirectAPIを使います。OleCreatePictureIndirectの場合、インターフェースの解放時にBitmapハンドルも合わせて解放するかどうかが選択出来ます。が今回はC#側にデータを渡すのでC#側で破棄するようにします。


次にC#側でIPicture/IPictureDispのBitmapハンドルを取り出す訳ですが、C#でIPicture/IPictureDispを利用するには、COMのOLE Automationを参照設定します。オブジェクトブラウザーでみると、IPictureは非表示の型とメンバーの表示にチェックを入れないと表示されません。C++側だとIPictureDispを使う際には、IDispatch::Invoke経由でアクセスする必要があるのでとっても不便です。どっちが良いのか迷う所。


IPictureDispを.NETの画像クラスに変換するには

    1. System.Drawing.Imageに変換する
    2. System.Windows.Media.BitmapSourceに変換する


ここまでやればBitmapをC++からC#に渡せるのですが、C#側でBackgroundスレッドを作成して、STAなC++のCOM DLLのI/FからIPictureDispを取得するとE_UNEXPECTED例外で落ちます。COMのスレッドアパートメントの問題かと思って、COM DLLをSTAからMTAに変更して見たところ、Backgroundスレッドからのアクセスは大丈夫だけど、メインスレッドからCOM I/Fの呼び出しを行うと今度はこっちでE_UNEXPECTED例外がでるように。色々調べたけど結局理由が分からず、諦めた。