COM でのイベント処理Event Handling in COM

使用してイベント ソースとイベント レシーバーを設定する COM イベントの処理で、 event_sourceevent_receiver指定それぞれ属性type = com.In COM event handling, you set up an event source and event receiver using the event_source and event_receiver attributes, respectively, specifying type=com. これらの属性により、カスタム、ディスパッチ、デュアルの各インターフェイスの適切なコードが挿入され、適用先のクラスでイベントを発生させ、COM 接続ポイントを通じてイベントを処理できるようになります。These attributes inject the appropriate code for custom, dispatch, and dual interfaces to allow the classes to which they are applied to fire events and handle events through COM connection points.

イベントの宣言Declaring Events

イベント ソース クラスで、使用、 _ _eventイベントとしてインターフェイスのメソッドの宣言に、インターフェイス宣言でキーワード。In an event source class, use the __event keyword on an interface declaration to declare that interface's methods as events. このインターフェイスのイベントは、インターフェイス メソッドとして呼び出されたときに発生します。The events of that interface are fired when you call them as interface methods. イベント インターフェイスのメソッドは、0 個以上のパラメーターを持つことができます (これはどれもパラメーター)。Methods on event interfaces can have zero or more parameters (which should all be in parameters). 戻り値の型は void または任意の整数型です。The return type can be void or any integral type.

イベント ハンドラーの定義Defining Event Handlers

イベント レシーバー クラスでは、イベント ハンドラーを定義します。イベント ハンドラーは、処理するイベントと一致するシグニチャ (戻り値の型、呼び出し規則、引数) を持つメソッドです。In an event receiver class, you define event handlers, which are methods with signatures (return types, calling conventions, and arguments) that match the event that they will handle. COM のイベントの呼び出し規約する必要はありませんが一致します。参照してくださいレイアウトに依存する COM イベント以下の詳細についてはします。For COM events, calling conventions do not have to match; see Layout Dependent COM Events below for details.

イベントへのイベント ハンドラーのフックHooking Event Handlers to Events

組み込み関数を使用する、イベント レシーバー クラスでも_ _hookにイベントをイベント ハンドラーに関連付けると_ _unhookイベント ハンドラーからイベントの関連付けを解除します。Also in an event receiver class, you use the intrinsic function __hook to associate events with event handlers and __unhook to dissociate events from event handlers. 複数のイベントを 1 つのイベント ハンドラーにフックすることも、複数のイベント ハンドラーを 1 つのイベントにフックすることもできます。You can hook several events to an event handler, or several event handlers to an event.

注意

通常、COM イベント レシーバーからイベント ソース インターフェイス定義へのアクセスを許可するには、2 つの方法があります。Typically, there are two techniques to allow a COM event receiver to access event source interface definitions. 最初の方法は、次に示すように、共通のヘッダー ファイルを共有することです。The first, as shown below, is to share a common header file. 2 つ目は、使用する#importで、embedded_idl修飾子をインポートして、属性が生成されたコードを保持でイベント ソースのタイプ ライブラリが .tlh ファイルに書き込まれます。The second is to use #import with the embedded_idl import qualifier, so that the event source type library is written to the .tlh file with the attribute-generated code preserved.

イベントの発生Firing Events

イベントを発生させるのだけで宣言されたインターフェイスでメソッドを呼び出す、 _ _eventキーワードは、イベント ソース クラス。To fire an event, simply call a method in the interface declared with the __event keyword in the event source class. ハンドラーがイベントにフックされている場合は、ハンドラーが呼び出されます。If handlers have been hooked to the event, the handlers will be called.

COM イベント コードCOM Event Code

次の例に、COM クラスでイベントを発生させる方法を示します。The following example shows how to fire an event in a COM class. 例をコンパイルして実行するには、コード内のコメントを参照してください。To compile and run the example, refer to the comments in the code.

// evh_server.h
#pragma once

[ dual, uuid("00000000-0000-0000-0000-000000000001") ]
__interface IEvents {
   [id(1)] HRESULT MyEvent([in] int value);
};

[ dual, uuid("00000000-0000-0000-0000-000000000002") ]
__interface IEventSource {
   [id(1)] HRESULT FireEvent();
};

class DECLSPEC_UUID("530DF3AD-6936-3214-A83B-27B63C7997C4") CSource;

次は、サーバーです。And then the server:

// evh_server.cpp
// compile with: /LD
// post-build command: Regsvr32.exe /s evh_server.dll
#define _ATL_ATTRIBUTES 1
#include <atlbase.h>
#include <atlcom.h>
#include "evh_server.h"

[ module(dll, name="EventSource", uuid="6E46B59E-89C3-4c15-A6D8-B8A1CEC98830") ];

[coclass, event_source(com), uuid("530DF3AD-6936-3214-A83B-27B63C7997C4")]
class CSource : public IEventSource {
public:
   __event __interface IEvents;

   HRESULT FireEvent() {
      __raise MyEvent(123);
      return S_OK;
   }
};

次は、クライアントです。And then the client:

// evh_client.cpp
// compile with: /link /OPT:NOREF
#define _ATL_ATTRIBUTES 1
#include <atlbase.h>
#include <atlcom.h>
#include <stdio.h>
#include "evh_server.h"

[ module(name="EventReceiver") ];

[ event_receiver(com) ]
class CReceiver {
public:
   HRESULT MyHandler1(int nValue) {
      printf_s("MyHandler1 was called with value %d.\n", nValue);
      return S_OK;
   }

   HRESULT MyHandler2(int nValue) {
      printf_s("MyHandler2 was called with value %d.\n", nValue);
      return S_OK;
   }

   void HookEvent(IEventSource* pSource) {
      __hook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler1);
      __hook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler2);
   }

   void UnhookEvent(IEventSource* pSource) {
      __unhook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler1);
      __unhook(&IEvents::MyEvent, pSource, &CReceiver::MyHandler2);
   }
};

int main() {
   // Create COM object
   CoInitialize(NULL);
   {
      IEventSource* pSource = 0;
      HRESULT hr = CoCreateInstance(__uuidof(CSource), NULL,         CLSCTX_ALL, __uuidof(IEventSource), (void **) &pSource);
      if (FAILED(hr)) {
         return -1;
      }

      // Create receiver and fire event
      CReceiver receiver;
      receiver.HookEvent(pSource);
      pSource->FireEvent();
      receiver.UnhookEvent(pSource);
   }
   CoUninitialize();
   return 0;
}

出力Output

MyHandler1 was called with value 123.
MyHandler2 was called with value 123.

レイアウトに依存する COM イベントLayout Dependent COM Events

レイアウトの依存関係は、COM プログラミングにおいてのみ考慮する項目です。Layout dependency is only an issue for COM programming. ネイティブおよびマネージド イベント処理では、ハンドラーのシグニチャ (戻り値の型、呼び出し規約、および引数) がイベントと一致する必要がありますが、イベント ハンドラー名はイベントと一致する必要はありません。In native and managed event handling, the signatures (return type, calling convention, and arguments) of the handlers must match their events, but the handler names do not have to match their events.

ただし、設定すると、COM イベント処理で、 layout_dependentパラメーターのevent_receivertrue名前とシグネチャの一致を適用します。However, in COM event handling, when you set the layout_dependent parameter of event_receiver to true, the name and signature matching is enforced. これは、イベント レシーバーのハンドラーの名前とシグニチャが、フックするイベントの名前とシグニチャに正確に一致する必要があることを意味します。This means that the names and signatures of the handlers in the event receiver must exactly match the names and signatures of the events to which they are hooked.

ときにlayout_dependentに設定されているfalse、呼び出し規約とストレージ クラス (virtual、static、およびなど) を混在し、実行されている間に一致するイベント メソッドとフック メソッド (そのデリゲートの場合)。When layout_dependent is set to false, the calling convention and storage class (virtual, static, and so on) can be mixed and matched between the firing event method and the hooking methods (its delegates). 若干効率的ですlayout_dependent=trueします。It is slightly more efficient to have layout_dependent=true.

たとえば、次のメソッドを持つ IEventSource が定義されているとします。For example, suppose IEventSource is defined to have the following methods:

[id(1)] HRESULT MyEvent1([in] int value);
[id(2)] HRESULT MyEvent2([in] int value);

イベント ソースが次の形式になっているとします。Assume the event source has the following form:

[coclass, event_source(com)]
class CSource : public IEventSource {
public:
   __event __interface IEvents;

   HRESULT FireEvent() {
      MyEvent1(123);
      MyEvent2(123);
      return S_OK;
   }
};

そこで、イベント レシーバーでは、次のように、IEventSource のメソッドにフックされるハンドラーは名前およびシグネチャを一致させる必要があります。Then, in the event receiver, any handler hooked to a method in IEventSource must match its name and signature, as follows:

[coclass, event_receiver(com, true)]
class CReceiver {
public:
   HRESULT MyEvent1(int nValue) {  // name and signature matches MyEvent1
      ...
   }
   HRESULT MyEvent2(E c, char* pc) {  // signature doesn't match MyEvent2
      ...
   }
   HRESULT MyHandler1(int nValue) {  // name doesn't match MyEvent1 (or 2)
      ...
   }
   void HookEvent(IEventSource* pSource) {
      __hook(IFace, pSource);  // Hooks up all name-matched events
                               // under layout_dependent = true
      __hook(&IFace::MyEvent1, pSource, &CReceive::MyEvent1);   // valid
      __hook(&IFace::MyEvent2, pSource, &CSink::MyEvent2);   // not valid
      __hook(&IFace::MyEvent1, pSource, &CSink:: MyHandler1); // not valid
   }
};

関連項目See also

イベント処理Event Handling