COM 中的事件处理

在 COM 事件处理中,使用 event_sourceevent_receiver 属性分别建立事件源和事件接收器并指定 type=com。这些属性插入相应的自定义接口代码、调度接口代码和双重接口代码,使其应用到的类可以通过 COM 连接点激发事件并处理事件。

声明事件

在事件源类中,在接口声明中使用 __event 关键字将该接口的方法声明为事件。当您将该接口的事件作为接口方法调用时将激发它们。事件接口上的方法可以具有零个或更多参数(这些参数均应为 in 参数)。返回类型可以是 void 类型或任何整型。

定义事件处理程序

在事件接收器类中定义事件处理程序,它们是一些方法,具有与要处理的事件相匹配的签名(返回类型、调用约定和参数)。对于 COM 事件,调用约定无需与之匹配;有关详细信息,请参见下面的与布局相关的 COM 事件

将事件处理程序与事件挂钩

同样是在事件接收器类中,使用内部函数 __hook 使事件与事件处理程序相关联,使用 __unhook 使事件与事件处理程序分离。可以将几个事件与一个事件处理程序挂钩,或者将几个事件处理程序与一个事件挂钩。

注意   通常情况下,有两种技术可以使 COM 事件接收器得以访问事件源接口定义。第一种技术(如下所示)是共享公共头文件。第二种技术是使用具有 embedded_idl 导入限定符的 #import,从而在保留属性生成的代码的情况下将事件源类型库写入 .tlh 文件。

激发事件

若要激发事件,只需在接口中调用在事件源类中用 __event 关键字声明的方法。如果该事件已与一些处理程序挂钩,则将调用这些处理程序。

COM 事件代码

下例显示如何在 COM 类中激发事件。若要编译并运行该示例,请参考代码中的注释。

// 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;
// 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;
   }
};
// 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("MyHandler1 was called with value %d./n", nValue);
      return S_OK;
   }

   HRESULT MyHandler2(int nValue) {
      printf("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;
}
输出
MyHandler1 was called with value 123.
MyHandler2 was called with value 123.

与布局相关的 COM 事件

布局依赖性只是一个针对 COM 编程的问题。在本机和托管事件处理中,处理程序的签名(返回类型、调用约定和参数)必须与其事件匹配,而处理程序名称则不必如此。

但是,在 COM 事件处理中,如果将 event_receiverlayout_dependent 参数设置为 true,则名称和签名匹配具有强制性。这意味着事件接收器中处理程序的名称和���名必须与处理程序挂钩到的事件的名称和签名完全匹配。

如果将 layout_dependent 设置为 false,则调用约定和存储类(虚拟、静态等等)在激发事件方法和挂钩方法(其委托)中可以混用和匹配。如果 layout_dependent=true,则效率将会略有提高。

例如,假设将 IEventSource 定义为具有下列方法:

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

假设事件源具有下列形式:

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

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

那么,在事件接收器中,挂钩到 IEventSource 的方法的处理程序必须与其名称和签名相匹配,如下所示:

[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
   }
};
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值