处理在COM的事件
在处理 COM 的事件,安装了,分别,使用 event_source 和 event_receiver 属性的事件源和事件接收器指定 type=COM。这些特性插入自定义的,计划相对应的代码,并且,允许将应用于激发事件和事件通过 COM 类的双重接口的连接点。
声明事件
在事件源类,请使用接口中声明的 __event 关键字声明接口的方法作为事件。,调用其用作接口方法时,该接口激发事件时。在事件接口的方法可以具有零或多个参数 (如果只是 在 参数)。返回类型可以是无效或任何整型。
定义事件处理程序
将事件接收器类,可以定义一个事件处理程序,与签名的方法 (返回类型,调用约定和参数) 符合事件它们将处理。对 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_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 事件
布局依赖项是 COM 编程中只有一个问题。在的本机和托管的事件,签名 (返回类型,调用约定和参数) 处理程序必须与它们的事件,但是,处理程序名称不必与其事件。
但是,在处理 COM 的事件,那么,当您设置 event_receiver 的 layout_dependent " 参数为 true时,名称和签名与强制实施。这意味着处理程序的名称和签名事件接收器的必须与它们挂钩事件的名称和签名。
当 layout_dependent 设置为 错误,调用约定,并存储类 (虚方法,静态,等等) 中混合搭配在激发事件方法和挂钩的方法 (其委托) 之间。效率稍高的具有 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
}
};