Ereignisbehandlung in COM
Bei der COM-Ereignisbehandlung richten Sie eine Ereignisquelle und einen Ereignisempfänger mithilfe der event_source
entsprechenden Attribute ein.type
=com
event_receiver
Diese Attribute fügen entsprechenden Code für benutzerdefinierte, dispatch- und duale Schnittstellen ein. Mit dem injizierten Code können die zugeordneten Klassen Ereignisse auslösen und Ereignisse über COM-Verbindungspunkte behandeln.
Hinweis
Ereignisattribute in systemeigenem C++ sind mit Standard C++ nicht kompatibel. Sie werden nicht kompiliert, wenn Sie den Konformitätsmodus angeben /permissive-
.
Deklarieren von Ereignissen
Verwenden Sie in einer Ereignisquellklasse die __event
Schlüsselwort (keyword) einer Schnittstellendeklaration, um die Methoden dieser Schnittstelle als Ereignisse zu deklarieren. Die Ereignisse der Schnittstelle werden ausgelöst, wenn Sie die Ereignisse als Schnittstellenmethoden aufrufen. Methoden für Ereignisschnittstellen können null oder mehr Parameter aufweisen (die alle in Parametern sein sollten). Der Rückgabetyp kann „void“ oder ein ganzzahliger Typ sein.
Definieren von Ereignishandlern
Sie definieren Ereignishandler in einer Ereignisempfängerklasse. Ereignishandler sind Methoden mit Signaturen (Rückgabetypen, Aufrufen von Konventionen und Argumenten), die mit dem Ereignis übereinstimmen, das sie behandeln. Für COM-Ereignisse müssen Aufrufkonventionen nicht übereinstimmen. Weitere Informationen finden Sie unter "Layoutabhängige COM-Ereignisse " weiter unten.
Binden von Ereignishandlern an Ereignisse
Außerdem verwenden Sie in einer Ereignisempfängerklasse die systeminterne Funktion __hook
, um Ereignisse mit Ereignishandlern zu verknüpfen und __unhook
die Zuordnung von Ereignissen von Ereignishandlern zu trennen. Sie können mehrere Ereignisse mit einem Ereignishandler oder mehrere Ereignishandler mit einem Ereignis verknüpfen.
Hinweis
Normalerweise gibt es zwei Möglichkeiten, um es einem COM-Ereignisempfänger zu ermöglichen, auf Schnittstellendefinitionen von Ereignisquellen zuzugreifen. Die erste Möglichkeit besteht in einer gemeinsamen Headerdatei, wie unten gezeigt. Die zweite besteht darin, #import mit dem embedded_idl
Importqualifizierer zu verwenden, sodass die Ereignisquelltypbibliothek in die TLH-Datei geschrieben wird, wobei der attributgenerierte Code beibehalten wird.
Auslösen von Ereignissen
Rufen Sie zum Auslösen eines Ereignisses eine Methode in der Schnittstelle auf, die mit der __event
Schlüsselwort (keyword) in der Ereignisquellklasse deklariert ist. Wenn Handler an das Ereignis geknüpft wurden, werden die Handler aufgerufen.
COM-Ereigniscode
Das folgende Beispiel zeigt, wie ein Ereignis in einer COM-Klasse ausgelöst wird. Informationen zum Kompilieren und Ausführen des Beispiels finden Sie in den Kommentaren im Code.
// 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;
Anschließend der Server:
// 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;
}
};
Und im Anschluss der Client:
// 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;
}
Ausgabe
MyHandler1 was called with value 123.
MyHandler2 was called with value 123.
Layoutabhängige COM-Ereignisse
Layoutabhängigkeit ist nur bei der COM-Programmierung ein Problem. Bei der systemeigenen und verwalteten Ereignisbehandlung müssen die Signaturen (Rückgabetyp, Aufrufkonvention und Argumente) der Handler mit ihren Ereignissen übereinstimmen, die Handlernamen müssen jedoch nicht mit ihren Ereignissen übereinstimmen.
Bei der COM-Ereignisbehandlung wird jedoch beim Festlegen des layout_dependent
Parameters auf event_receiver
" true
Name" und "Signaturabgleich" erzwungen. Die Namen und Signaturen der Handler im Ereignisempfänger und in den hooked-Ereignissen müssen exakt übereinstimmen.
Wenn layout_dependent
diese Einstellung festgelegt false
ist, kann die Aufrufkonvention und die Speicherklasse (virtuell, statisch usw.) zwischen der Auslösenden Ereignismethode und den Hookingmethoden (den zugehörigen Delegaten) gemischt und abgeglichen werden. Es ist etwas effizienter zu haben layout_dependent
=true
.
Nehmen Sie z. B. an, IEventSource
wird definiert, um über die folgenden Methoden zu verfügen:
[id(1)] HRESULT MyEvent1([in] int value);
[id(2)] HRESULT MyEvent2([in] int value);
Angenommen, die Ereignisquelle weist das folgende Format auf:
[coclass, event_source(com)]
class CSource : public IEventSource {
public:
__event __interface IEvents;
HRESULT FireEvent() {
MyEvent1(123);
MyEvent2(123);
return S_OK;
}
};
Im Ereignisempfänger muss dann jeder Handler, der an eine Methode IEventSource
gebunden ist, dem zugehörigen Namen und der zugehörigen Signatur entsprechen, wie hier gezeigt:
[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
}
};
Siehe auch
Feedback
https://aka.ms/ContentUserFeedback.
Bald verfügbar: Im Laufe des Jahres 2024 werden wir GitHub-Issues stufenweise als Feedbackmechanismus für Inhalte abbauen und durch ein neues Feedbacksystem ersetzen. Weitere Informationen finden Sie unterFeedback senden und anzeigen für