2012年8月4日土曜日

ActiveXコントロールをOLEオートメーションで呼び出す方法


ActiveXコントロールをダイアログに張り付けず、 ダイアログなしのオートメーションサーバーとして呼び出したい
場合のやり方についてです。試したのはイベントを含んでいないケース(プロパティとメソッドのみ)なので、
イベントをサポートしている場合にどういった挙動になるかは調べてません。

これ普通のDLLでよかったんじゃ・・・。というActiveXコントロールを、マルチスレッドでの利用のために
オートメーションを使った呼び出しに変更した時のお話です。
知ってれば1分で終わる話ですが、少し調べるのに手間取ったのでメモしておきます。
ちなみに対象のActiveXコントロールのソースコードにアクセスできて、ビルド可能な事が前提です。

結論から書くと、ActiveX側のCOleControlから派生しているクラスで、IsInvokeAllowedを
オーバーライドしてTRUEを返すだけです。
--
以下は試した内容です。

①タイプライブラリの作成
タイプライブラリを使用しますので、ActiveXコントロールをビルドしてもtlbファイルが生成されていない場合、
プロジェクトのプロパティから、MIDLの出力設定へ進み、タイプライブラリを生成する設定にします。

②ラッパークラスの作成
クラスウィザードで、MFCy→「TypeLib からの MFC クラス」を選び、
タイプライブラリを選択してラッパークラスを作成します。
COleDispatchDriverから派生したクラスが自動生成されているはずです。


③ラッパークラスの調整
一番上に#import文があると思いますが、今回はCreateDispatchを呼ぶ際のGUIDを取得する以外には使いませんので、
ラッパークラスを生成しない様に下記の様なオプションを設定しておきます。

#import "xxx.tlb" no_namespace named_guids no_implementation no_smart_pointers raw_interfaces_only

ビルドすると、出力フォルダにxxx.tlhというファイルが自動生成されます。
これをエディタで開き、CLSID_から始まるGUIDを探します。

④使ってみる
ActiveXコントロールと接続するためには、下記にように書きます。
// テストコード
// (自動生成されたクラス名を仮にCMyOle、GUIDをCLSID_XXXとします)
CMyOle myObj;
if(myObj.CreateDispatch(CLSID_XXX) == TRUE){
    myObj.FuncA();
}
多分FuncAの呼び出しでCOleException (エラーコード 0x8000FFFF) が発生したと思います。
CreateDispatchに失敗する場合は、COMが登録されていない(regsvr32)か、CLSIDが
間違っている可能性があります。(ここで指定すべきはIIDではなくCLSIDです)

⑤ActiveXコントロールの変更
// COleControlから派生しているクラスでIsInvokeAllowedを
// オーバーライドしてTRUEを返す
BOOL CMyOleControl::IsInvokeAllowed (DISPID)
{
    return TRUE;
}
⑥再度試す
ビルドしなおしてテストすると、今度は④のコード呼び出しが成功しました。

【参考】
How to use an OLE control as an automation server in Visual C++
http://support.microsoft.com/kb/146120/EN-US

0 件のコメント:

コメントを投稿