動的なCOMのイベント取得について

[戻る]
飛んできたどらやき 2009/08/31(月) 10:57:50
動的なCOMから飛んできたイベントによって
処理を行いたいです。

例えば
COM生成がされているのを前提として
以下のCalcメソッドの関連付け(実際の関数の受け口)は
どうやって実装すればいいのですか?

struct __declspec(uuid("d1890a6c-b213-11d4-afc1-00d0b7ad59e8"))
_Class1 : IDispatch
{
    //
    // Raw methods provided by interface
    //

    virtual HRESULT __stdcall Calc (
        long x,
        long y,
        long * _arg3 ) = 0;
};

ご教授お願いします。

aetos 2009/08/31(月) 13:32:20
この場合、Calc を呼ぶとその中からイベントが発生するのですか?

飛んできたどらやき 2009/08/31(月) 13:58:11
説明不足ですみません。

OCX側からCalcを呼び出し、
OCXをディスパッチしている
メインプログラム側が受け取るパターンです。

いくつか方法があるようですが、
現在作成しているプロジェクトですと
tlhファイル、tliファイルにそのヘッダ情報らしき、
コードがコンパイル毎に作成されます。

その内容をメインプログラム側と関連付ける方法が
分かればなんとかなりそうですが、、、
(継承クラスの作成??)

ご存じでしたら教えて頂けませんか?

aetos 2009/08/31(月) 17:08:53
んーと…やっぱりよくわかりません。

COM でイベントを扱う際のキーワードをいくつか挙げておきます。
・IConnectionPoint / IConnectionPointContainer インターフェイス
・IDL の [source] 属性

subaru 2009/08/31(月) 17:18:58
COMのイベントは IDispatch::Invoke で呼び出されるので
イベント受信用のクラスを自分で用意することになります。
(それを IConnectionPoint::Advise で登録する)

#tlh/tliファイルに生成されたものの使い方は私も知りません。

飛んできたどらやき 2009/08/31(月) 18:15:33
お返事ありがとうございます。

COMの事についてあまり理解していないもので、
どうすればといった状況です。。。

今、OCXに関連したヘッダファイルがあり、
それをもとにどのように使えばいいのか探しています。

その中ではPreSubclassWindowメソッドないで
QueryInterfaceを読んでいます。

おそらく#importしているので

>・IDL の [source] 属性

を用いた方法で扱ってるのではないかと、考えてます。


>COMのイベントは IDispatch::Invoke で呼び出されるので
>イベント受信用のクラスを自分で用意することになります。
>(それを IConnectionPoint::Advise で登録する)

現在COMの呼び出しイベントは設けてあり、
使えるのですが、今回のようなイベント受信用の機能を追加するときも、
改めてクラスを作成するのでしょうか?

なんとも、分かりずらい説明になってしまってすみません。。。

subaru 2009/09/01(火) 09:50:28
>現在COMの呼び出しイベントは設けてあり、
>使えるのですが、今回のようなイベント受信用の機能を追加するときも、
>改めてクラスを作成するのでしょうか?

_Class1::Calc は自分で呼ぶものではなくメインプログラム側で
ocxのイベントととして呼ばれることを期待しているということですよね?
しかし _Class1 を実装しようとしても結局コンパイラに
IDispatch を実装しろって怒られてしまうと思うのですが・・・
#だから使い方がよくわからない

なのでイベントの実装は _Class1 から派生して作っても
IDispatch から直接派生して作ってもたいした違いは
ないんじゃないかと思えるのです。

ITO 2009/09/01(火) 10:28:46
うーん、
COMのことはよく分からないのですが、

>今、OCXに関連したヘッダファイルがあり、
>それをもとにどのように使えばいいのか探しています。

というなら、まず
  1.提供されたファイル(どんな拡張子のものか)を掲示したほうがいいですね。
  2.提供者は、会社の関連の方ですか?、ネットからダウンロードしてきたか?
    製品として購入(ダウンロード販売も含む)

  3.提供者はどうのようにリンクするか言ってきてますか?
  4.もともと、VBようですか?
について情報を掲示したほうがいいと思います。

subaru 2009/09/01(火) 10:45:31
>今、OCXに関連したヘッダファイルがあり、
というのは単にタイプライブラリから
生成されたファイルのことと解釈してました(?)

イベントを拾うために最低限必要なのは
IUnknown の実装と IDispatch::Invoke の実装です。
(他は E_NOTIMPL を返すようにしておけばよい)
#必要な ID などは OLE/COMオブジェクトビューアで確認できます。

COMのイベント呼び出しの仕組みはこちらです。
http://msdn.microsoft.com/ja-jp/library/cc482696.aspx

飛んできたどらやき 2009/09/01(火) 11:48:33
皆様、本当にお返事有難うございます。

>_Class1::Calc は自分で呼ぶものではなくメインプログラム側で
>ocxのイベントととして呼ばれることを期待しているということですよね?

はい、その通りです。
また、COMイベントの仕組みについて見させて頂きました。

>  1.提供されたファイル(どんな拡張子のものか)を掲示したほうがいいですね。
>  2.提供者は、会社の関連の方ですか?、ネットからダウンロードしてきたか?
>    製品として購入(ダウンロード販売も含む)
>  3.提供者はどうのようにリンクするか言ってきてますか?
>  4.もともと、VBようですか?

提供ファイルはOCXとヘッダファイルです。
取得・設定関数にてget=xxx , put=yyyと記述されているので、
おそらくVBかと思います。

>なのでイベントの実装は _Class1 から派生して作っても
>IDispatch から直接派生して作ってもたいした違いは
>ないんじゃないかと思えるのです。

>イベントを拾うために最低限必要なのは
>IUnknown の実装と IDispatch::Invoke の実装です。
>(他は E_NOTIMPL を返すようにしておけばよい)
>#必要な ID などは OLE/COMオブジェクトビューアで確認できます。

今回、提供ヘッダファイル側でCreateInstanceを行い、
QueryInterfaceで読みだしているので、
IDispatch::Invokeとは異なる方法で読みだしているのでは?
と解釈しているのは誤りでしょうか。。。

ITO 2009/09/01(火) 13:17:33
過去ログにありました。
http://rararahp.cool.ne.jp/cgi-bin/lng/vc/vclng.cgi?print+200208/02080099.txt

subaru 2009/09/01(火) 13:29:58
>今回、提供ヘッダファイル側でCreateInstanceを行い、
>QueryInterfaceで読みだしているので、
>IDispatch::Invokeとは異なる方法で読みだしているのでは?
>と解釈しているのは誤りでしょうか。。。

CreateInstanceしているのはイベントを呼ぶ側の
オブジェクトのことですよね。
(その実態から得られるインターフェースを通じて
操作できるだけでIDispatchかどうかは関係ない)

イベントの実体はOCX側から提供してもらうものではなく
あなたが提供するものです。

bun 2009/09/02(水) 10:56:16
イベントとかが話に出てくるので、
単純なCOMではなく、ActiveXのようなものかな?

そうだとして、見えてこないのが、
処理を実装したいのは、
ActiveX側のプロジェクト?
それともActiveXを使用する側のプロジェクト?

想像だけで話してるので大きく外していたらすいません。

subaru 2009/09/02(水) 13:21:53
OCXファイルとのなので ActiveXコントロールのことだと思われます。

ただ、COMで起動した Internet Explorer のイベント処理方法と
WebBrowser コントロールのイベント処理方法が同じであることを考えると
コントロールかどうかというのは重要なことではないかもしれませんが。

VBなんかだと画面に貼り付けるだけで簡単にイベント処理できるけど
これはそのような仕組みが用意されているからでしょう。

#importで自動生成される tlh/tliファイルが
IDispatch::Invoke から各メソッドにリダイレクトするような
実装まで用意してくれていれば簡単なのですが
どうやらそこまでは面倒見てくれないようです。

ATLクラス(IDispEventImplなど?)を使えばMFCのメッセージマップの機能のような
ものも使えるようなのでもう少し簡単になるかもしれません。

飛んできたどらやき 2009/09/02(水) 15:28:44
皆様、コメント本当に有難うございます。

あれから、過去ログ等を参考にしながら、
COM機能の習得にいそしんでいます。

>イベントとかが話に出てくるので、
>単純なCOMではなく、ActiveXのようなものかな?


>OCXファイルとのなので ActiveXコントロールのことだと思われます。

はい、通常はダイアログリソース等にコントロールを張り付けて
動作させるので、ActiveXコントロールです。

>ATLクラス(IDispEventImplなど?)を使えばMFCのメッセージマップの機能のような
>ものも使えるようなのでもう少し簡単になるかもしれません。

静的なコントロールを用いる場合、
ON_EVENT等でリソースIDとイベントNoを指定すれば、
単純に飛んできます。

ただし、今回は動的にこのコントロールを作成したいもので、
その際の関連付けの方法を探しているところです。。。

どこかで(Invoke等を用いて)イベント通知の割り当てを
するような感じかなぁと考えてますが。。。

bun 2009/09/03(木) 09:31:16
> 静的なコントロールを用いる場合、
> ON_EVENT等でリソースIDとイベントNoを指定すれば、
> 単純に飛んできます。
> 
> ただし、今回は動的にこのコントロールを作成したいもので、
> その際の関連付けの方法を探しているところです。。。

なるほど。。。
ようやく質問内容が分かりました。

そして、私では役に立たないと言うことも ^^;

bun 2009/09/03(木) 10:11:33
> そして、私では役に立たないと言うことも ^^;
と、言いつつ、私が所有しているActiveXプロジェクトをデバッグしてみました。

なんとなくですが、
ActiveXコントロールを登録することにより、ON_EVENTがキックされるようになる
のかなぁ?そんな風に見えます。
で、登録するためのメソッドが CWnd::CreateControl() なのかな?

全部、憶測&未検証ですいません。

subaru 2009/09/03(木) 10:21:34
>静的なコントロールを用いる場合、
>ON_EVENT等でリソースIDとイベントNoを指定すれば、
>単純に飛んできます。

MFCだとこの辺は隠蔽されているけど、ダイアログ生成時に
内部で生成する IDispatch の実装(COleControlSite のインナークラス?)
との関連付けを行っているような感じです(自信なし)

ここでのイベントNoというのがDISPIDのことで実際には
このIDと引数が IDispatch::Invoke に渡されてきてイベントと認識しています。
(IDがどのメソッドに対応するかはタイプライブラリに書いてある)

>ただし、今回は動的にこのコントロールを作成したいもので、
>その際の関連付けの方法を探しているところです。。。

イベントの実装があるなら、GetContorolUnkown で得られるポインタから
QueryInterface で IConnectionPointContainer のポインタを得る。
IConnectionPointContainer::FindConnectionPoint で
IConnectionPoint のポインタが得られたら、Advise/Unadvise で
イベントとの関連付け/解除を行う。
・・・という手順をATL/MFCのヘルパー関数とかでもうちょっと簡略化できるかもしれま
せん。

飛んできたどらやき 2009/09/03(木) 19:52:25
subaruさん、bunさん
お返事有難うございます。

>イベントの実装があるなら、GetContorolUnkown で得られるポインタから
>QueryInterface で IConnectionPointContainer のポインタを得る。
>IConnectionPointContainer::FindConnectionPoint で
>IConnectionPoint のポインタが得られたら、Advise/Unadvise で
>イベントとの関連付け/解除を行う。

非常に分かり易い解説有難うございます。
COMの接続を試みてAtlAdvise/AtlUnadviseでしょうか。
これで関連付けを行っています。

ここまで来たので何とかなりそうな気がします。

sabaruさん有難うございます。

飛んできたどらやき 2009/09/04(金) 09:48:41
[[解決]]
何とかイベントを受けることができました。

有難うございました。

bun 2009/09/04(金) 11:45:04
興味深かったので、追跡調査してみました。

結果、以下の手順でできました。
(以下の例は、ダイアログベースアプリに、ActiveXをCreate()する場合)

1) ActiveXのラッパクラスを作成
     VC++2005なら、[変数の追加]時に自動作成される。
     私の場合、クラス名[CGrid], 変数名[m_Grid]とした     <- m_Gridは静的生成
2) 動的生成用の変数を追加
     私の場合、クラス名[CGrid], 変数名[m_TestGrid]とした <- m_TestGridは動的生
成
3) 動的生成するActiveXのリソースIDを定義
     私の場合、以下のコードを追加
     #define ID_TESTGRID (WM_APP + 1)
4) ダイアログのOnInitDialog()にて、ActiveXの動的生成
     具体的には、以下のコードを追加
     m_TestGrid.Create(_T("TestGrid"),
         WS_VISIBLE, CRect(10, 10, 420, 240), this, ID_TESTGRID);
5) ON_EVENTの追加
     私の場合、以下のようなマッピングとしました
     ON_EVENT(CxxxDlg, ID_TESTGRID, 24, CxxDlg::AfterEditGrid, VTS_I4 VTS_I4)

これで、上記ActiveXの対象イベントが発生すると、
  CxxDlg::AfterEditGrid()
が呼ばれるようになりました。

subaru 2009/09/04(金) 13:51:09
>bunさん
なるほど。ActiveXのCreate()だけでマッピングでの
関連付けも行っていたのですね。

しかしマッピングの部分はマクロで固定の配列を作っていて
動的な配列の書き換えはできなさそう。
あらかじめ動的生成するコントロールの個数とIDがわかっていないと
使いづらいかもしれません。

subaru 2009/09/04(金) 14:15:35
同じIDでコントロールを作ることもできますね。。。
先ほどの書き込みは無視してください。

飛んできたどらやき 2009/09/07(月) 09:32:48
CDialogWnd::Createメソッドにそのような
機能があるんですね。
(実際にはCWndでしょうか。)

ActiveXの管理ラッパクラスをCWndで継承するような、
機構だと非常に簡単に作れそうですね。

非常に勉強になります。

古いスレッドには追加発言できません。