在 COM 中處理事件
在 COM 事件的處理,您將建立事件來源和事件接收者使用 event_source 和 event_receiver 分別屬性,指定type=com。 這些屬性插入適當的程式碼,自訂、 分派,以及雙重介面,可讓它們套用到引發事件和處理透過 COM 連接點事件的類別。
宣告事件
在 [事件來源類別中,使用 __event 關鍵字宣告這個介面的方法,為事件介面宣告上。 當您為介面方法呼叫時,會引發該介面的事件。 事件介面上的方法可以有零個或多個參數 (這應該都是在參數)。 傳回的型別可以是虛值或任何整數類資料型別。
定義事件處理常式
在事件接收者類別中,您可以定義事件處理常式,以符合將處理事件的簽章 (傳回型別,呼叫慣例和引數) 的方法。 COM 事件的呼叫慣例不需要符合。 請參閱配置相依 COM 事件下面如需詳細資訊。
攔截到事件的事件處理常式
也在事件接收者類別中,您要使用內建的函式 __hook 事件與事件處理常式和 __unhook ,就從事件處理常式的事件。 您可以連結至事件處理常式的幾個事件或事件的數個事件處理常式。
注意事項 |
---|
一般而言,有兩種技術允許 COM 事件接收者來存取事件來源介面定義。第一,如下所示是共用的通用標頭檔。第二個是使用 # import 與embedded_idl匯入辨識符號,使事件來源的型別程式庫就會寫入.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 程式設計的問題。 在原生和 managed 事件處理簽章 (傳回型別、 呼叫慣例和引數) 的處理常式必須符合它們的事件,但不是需要符合它們的事件處理常式的名稱。
不過,在 COM 事件處理,當您設定 layout_dependent 參數的 event_receiver 到 ,則為 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
}
};