Obsługa zdarzeń w modelu COM
W obsłudze zdarzeń COM, ustaw źródło i zdarzenie odbiornika zdarzenia za pomocą atrybutów źródło_zdarzenia i event_receiver, odpowiednio, określając type=com.Te atrybuty wprowadzają odpowiedni kod dla niestandardowych, wysyłanych i dualnych interfejsów, klas, aby zezwolić, do których są stosowane do wywołania zdarzenia i obsługi zdarzeń za pośrednictwem punktów połączenia COM.
Deklarowanie zdarzeń
W klasie źródła zdarzeń, użyj słowa kluczowego klasy __event w deklaracji interfejsu do deklarowania metod tego interfejsu jako wydarzenia.Ten interfejs jest uruchamiany podczas wywoływania metody interfejsu.Metody interfejsów zdarzeń mogą mieć zero lub więcej parametrów (które powinny być w parametrach).Zwracany typ może być typu void lub każdym całkowitym.
Definiowanie obsługi zdarzeń
W klasie odbiornika zdarzenia należy zdefiniować procedury obsługi zdarzeń, które są metodami z podpisami (zwracanych typów, wywołującymi konwencje i argumentami), które odpowiadają zdarzeniu, które będą obsługiwać.W przypadku zdarzeń COM konwencje wywoływania nie muszą sobie odpowiadać; zobacz Układ zależnych zdarzeń COM, aby uzyskać szczegółowe informacje.
Podłączanie zdarzeń obsługi zdarzeń
Również w klasą odbiornika zdarzenia funkcja wewnętrzna __hook umożliwia skojarzenie zdarzeń z obsługą zdarzeń i __unhook, aby odłączyć zdarzenia z obsługi zdarzeń.Można podłączyć kilka zdarzeń do programu obsługi zdarzeń lub obsługi zdarzeń do zdarzenia.
[!UWAGA]
Zwykle istnieją dwie techniki umożliwiające odbiorcy zdarzenia COM definicje interfejsu źródła zdarzeń dostępu.Po pierwsze, jak pokazano poniżej, jest to udostępnienie wspólnego pliku nagłówka.Drugim jest użycie #import z kwalifikatora importu embedded_idl, tak aby biblioteki typów źródła zdarzeń były zapisywane do pliku .tlh, z zachowanym kodem generowanych atrybutów.
Wyzwalanie zdarzenia
Aby wyzwolić zdarzenie, po prostu wywołaj metodę w zadeklarowanym interfejsie za pomocą słowa kluczowego __event w źródle zdarzenia klasy.Jeśli programy obsługi zostały podłączone do zdarzenia, zostanie wywołana metoda obsługi.
Kod zdarzenia COM
Poniższy przykład pokazuje jak wywołać zdarzenie klasy COM.Aby skompilować i uruchomić przykład, zobacz komentarze w kodzie.
// 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;
A następnie serwera:
// 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;
}
};
A następnie klienta:
// 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;
}
Dane wyjściowe
MyHandler1 was called with value 123.
MyHandler2 was called with value 123.
Zdarzenia zależne od modelu układu COM
Zależność układu jest tylko problemem programowania COM.W obsłudze zdarzeń macierzystychi zarządzanych, podpisy (typ wartości zwracanej, konwencja wywołania i argumenty) z programów obsługi muszą odpowiadać ich zdarzeniom, ale nazwy programu obsługi nie muszą odpowiadać ich zdarzeniom.
Jednakże, w obsłudze zdarzeń COM, po ustawieniu parametru layout_dependent z event_receiver do true, nazwa i podpis dopasowania są wymuszane.Oznacza to, że nazwy i wzory podpisów w programach obsługi zdarzeń muszą dokładnie odpowiadać nazwom i wzorom podpisów zdarzeń, do których są podłączone.
Gdy layout_dependent jest ustawiona na false, wywołania klasy konwencji i pamięci (wirtualna, statyczna i itd.) mogą być mieszane i dopasowywane między wypalaniami metody zdarzeń i metody podłączania (swoich przedstawicieli).Jest to nieco bardziej efektywne, mając layout_dependent=true.
Załóżmy na przykład, że IEventSource jest zdefiniowany dla następujących metod:
[id(1)] HRESULT MyEvent1([in] int value);
[id(2)] HRESULT MyEvent2([in] int value);
Przyjęto założenie, że źródło zdarzenia ma następującą postać:
[coclass, event_source(com)]
class CSource : public IEventSource {
public:
__event __interface IEvents;
HRESULT FireEvent() {
MyEvent1(123);
MyEvent2(123);
return S_OK;
}
};
Następnie, w przypadku odbiornika, każdy program obsługi, który podłączany jest do metody w IEventSource musi odpowiadać jej nazwie i podpisie, w następujący sposób:
[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
}
};