Freigeben über


Ereignisbehandlung in COM

Bei der COM-Ereignisbehandlung richten Sie eine Ereignisquelle und einen Ereignisempfänger mithilfe der event_source entsprechenden Attribute ein.type=comevent_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 " trueName" 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 falseist, 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

Behandlung von Ereignissen