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=comwartość . 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_dependentevent_receiver na truewartość , 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 falsewartość , 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ż

Obsługa zdarzeń