Obsługa zdarzeń w modelu COM
W przypadku obsługi zdarzeń MODELU COM należy skonfigurować odpowiednio źródło zdarzeń i odbiornik zdarzeń przy użyciu event_source
atrybutów i event_receiver
określających type
=com
wartość . Te atrybuty wprowadzają odpowiedni kod dla interfejsów niestandardowych, dyspozytorskich i podwójnych. Wstrzyknięty kod umożliwia klasom przypisanym wyzwalanie zdarzeń i obsługę zdarzeń za pośrednictwem punktów połączenia COM.
Uwaga
Atrybuty zdarzeń w natywnym języku C++ są niezgodne z językiem Standard C++. Nie są one kompilowane podczas określania /permissive-
trybu zgodności.
Deklarowanie zdarzeń
W klasie źródła zdarzeń użyj __event
słowa kluczowego w deklaracji interfejsu, aby zadeklarować metody tego interfejsu jako zdarzenia. Zdarzenia tego interfejsu są uruchamiane podczas wywoływania ich jako metod interfejsu. Metody interfejsów zdarzeń mogą mieć zero lub więcej parametrów (które powinny być we wszystkich parametrach). Zwracany typ może być typu void lub dowolnego całkowitego.
Definiowanie programów obsługi zdarzeń
Programy obsługi zdarzeń definiuje się w klasie odbiorcy zdarzeń. Programy obsługi zdarzeń to metody z podpisami (typy zwracane, konwencje wywoływania i argumenty), które odpowiadają zdarzeniu, które będą obsługiwane. W przypadku zdarzeń COM konwencje wywoływania nie muszą być zgodne. Aby uzyskać więcej informacji, zobacz Zdarzenia COM zależne od układu poniżej.
Podłączanie programów obsługi zdarzeń do zdarzeń
Ponadto w klasie odbiorcy zdarzeń funkcja wewnętrzna __hook
służy do kojarzenia zdarzeń z procedurami obsługi zdarzeń i __unhook
usuwanie skojarzenia zdarzeń z programów obsługi zdarzeń. Można podłączyć kilka zdarzeń do programu obsługi zdarzeń lub kilka programów obsługi zdarzeń do zdarzenia.
Uwaga
Zwykle istnieją dwie techniki udostępniające odbiorcy zdarzenia COM definicje interfejsu źródła zdarzeń. Pierwszą techniką, jak pokazano poniżej, jest udostępnienie wspólnego pliku nagłówkowego. Drugim jest użycie #import z kwalifikatorem embedded_idl
importu, aby biblioteka typów źródła zdarzeń została zapisana w pliku tlh z zachowanym kodem wygenerowanym przez atrybuty.
Wyzwalanie zdarzeń
Aby uruchomić zdarzenie, wywołaj metodę w interfejsie zadeklarowanym za pomocą __event
słowa kluczowego w klasie źródła zdarzeń. Jeśli programy obsługi zostały podłączone do zdarzenia, zostaną wywołane programy obsługi.
Kod zdarzenia COM
Poniższy przykład pokazuje jak wywołać zdarzenie w klasie 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 COM zależne od układu
Zależność układu jest tylko problemem programowania COM. W obsłudze zdarzeń natywnych i zarządzanych podpisy (typ zwracany, konwencja wywoływania i argumenty) programów obsługi muszą być zgodne ze swoimi zdarzeniami, ale nazwy programu obsługi nie muszą być zgodne ze swoimi zdarzeniami.
Jednak w przypadku obsługi zdarzeń COM, po ustawieniu parametru layout_dependent
event_receiver
na true
wartość , jest wymuszane dopasowanie nazwy i podpisu. Nazwy i podpisy programów obsługi w odbiorniku zdarzeń i w podłączonych zdarzeniach muszą być dokładnie zgodne.
Gdy layout_dependent
jest ustawiona false
wartość , konwencję wywoływania i klasę magazynu (wirtualną, statyczną i tak dalej) można mieszać i dopasowywać między metodą wyzwalania zdarzeń a metodami zaczepienia (jej delegatami). Jest nieco bardziej wydajne, aby mieć .layout_dependent
=true
Załóżmy, ż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 podłączony 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
}
};
Zobacz też
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla