Sdílet prostřednictvím


Zpracování událostí v modelu COM

Ve zpracování událostí modelu COM lze nastavit zdroj a příjemce události pomocí atributů event_source a event_receiver a zadáním parametru type=com.Tyto atributy vloží příslušný kód pro vlastní, odeslání a duální rozhraní umožňující třídám, na které se vztahují, vyvolat a zpracovat události prostřednictvím přípojných bodů modelu COM.

Deklarace událostí

Ve třídě zdroje události, použijte klíčové slovo __event při deklaraci rozhraní, čímž metody tohoto rozhraní deklarujete jako události.Události tohoto rozhraní jsou aktivovány, pokud je zavoláte jako metody rozhraní.Metody v rozhraních událostí mohou mít žádné nebo více parametrů (které by všechny měly být parametry in).Návratový typ může být typ void nebo libovolný celočíselný typ.

Definice obslužných rutin událostí

V třídě příjemce události definujte obslužné rutiny událostí, což jsou s metody s podpisy (návratové typy, úmluvy volání a argumenty) odpovídajícími události, kterou budou zpracovávat.Konvence volání událostí modelu COM nemusí být stejné. Další informace naleznete níže v části Události modelu COM závislé na rozložení.

Připojení obslužných rutin událostí k událostem

V třídě příjemce události lze také použít vnitřní funkci __hook k připojení událostí k obslužným rutinám událostí a funkci __unhook k odpojení od obslužných rutin událostí.Lze připojit několik událostí k obslužné rutině události nebo několik obslužných rutin událostí k události.

[!POZNÁMKA]

Obvykle jsou k dispozici dvě techniky umožňující příjemci události modelu COM přistupovat k definicím rozhraní zdroje událostí.Prvním, jak je uvedeno níže, je sdílet společný soubor hlaviček.Druhým je použití příkazu #import s kvalifikátorem importu embedded_idl, takže knihovna typů zdroje události je zapsána do souboru .tlh se zachovaným kódem, který je tímto atributem generován.

Vyvolání událostí

Chcete-li vyvolat událost, jednoduše zavolejte metodu v rozhraní deklarovaném pomocí klíčového slova __event v třídě zdroje události.Pokud byly obslužné rutiny připojeny k této události, budou tyto obslužné rutiny zavolány.

Kód události modelu COM

Následující příklad ukazuje, jak vyvolat událost v třídě modelu COM.Chcete-li příklad zkompilovat a spustit, přečtěte si komentáře v kódu.

// 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 poté tento 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;
   }
};

A poté tohoto 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;
}

Výsledek

MyHandler1 was called with value 123.
MyHandler2 was called with value 123.

Události modelu COM závislé na rozložení

Závislost na rozložení se týká pouze programování v modelu COM.Při zpracování nativních a spravovaných událostí musí podpisy (návratový typ, konvence volání a argumenty) obslužných rutin odpovídat jejich událostem, ale názvy obslužných rutin nemusí odpovídat jejich událostem.

Ačkoliv když při zpracování událostí modelu COM nastavíte parametr layout_dependent atributu event_receiver na hodnotu true, je vynucena shoda jména a podpisu.To znamená, že názvy a podpisy obslužných rutin v příjemci události musí přesně odpovídat názvům a podpisům událostí, ke kterým jsou připojeny.

Když je parametr layout_dependent nastaven na hodnotu false, úmluvu volání a třídu úložiště (virtuální, statická a tak dále) lze kombinovat a může nastat shoda mezi vyvolanou metodou události a metodami připojení (jejich delegáty).Mírně efektivnější je mít parametr nastaven na layout_dependent=true.

Předpokládejme například, že je definováno rozhraní IEventSource s následujícími metodami:

[id(1)] HRESULT MyEvent1([in] int value);
[id(2)] HRESULT MyEvent2([in] int value);

Předpokládejme, že zdroj události má následující tvar:

[coclass, event_source(com)]
class CSource : public IEventSource {
public:
   __event __interface IEvents;

   HRESULT FireEvent() {
      MyEvent1(123);
      MyEvent2(123);
      return S_OK;
   }
};

Poté v příjemci události musí kterákoli obslužná rutina připojená k metodě v rozhraní IEventSource odpovídat jejímu názvu a podpisu následovně:

[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
   }
};

Viz také

Referenční dokumentace

Zpracování událostí