Обработка событий в COM

При обработке COM-событий вы настраиваете источник события и приемник событий с помощью event_sourceevent_receiver атрибутов и соответственно, указывая type=com . Эти атрибуты вставляют соответствующий код для пользовательских, диспетчеризации и сдвоенных интерфейсов. Внедренный код позволяет классам с атрибутами вызывать события и управлять событиями через точки подключения COM.

Примечание

Атрибуты событий в машинном коде C++ несовместимы со стандартным C++. Они не компилируются при указании /permissive- режима соответствия.

Объявление событий

В классе источника событий используйте __event ключевое слово в объявлении интерфейса, чтобы объявить методы интерфейса как события. События этого интерфейса запускаются при вызове их в качестве методов интерфейса. Методы в интерфейсах событий могут иметь ноль или более параметров (которые должны быть в параметрах). Тип возвращаемого значения может быть пустым или любым целочисленным типом.

Определение обработчиков событий

Обработчики событий определяются в классе приемника событий. Обработчики событий — это методы с сигнатурами (возвращаемыми типами, соглашениями о вызовах и аргументами), которые соответствуют событию, которое они будут обработаны. Для COM-событий соглашения о вызовах не должны совпадать. Дополнительные сведения см. в разделе зависимые от макета события com ниже.

Подключение обработчиков событий к событиям

Кроме того, в классе приемника событий используется встроенная функция __hook для связывания событий с обработчиками событий и для разрыва __unhook связи между событиями из обработчиков событий. Можно прикрепить несколько событий к обработчику событий либо несколько обработчиков событий к одному событию.

Примечание

Как правило, существует два метода предоставления приемнику событий COM доступа к определениям интерфейса исходного кода событий. Во-первых, как показано ниже, можно предоставить общий доступ к одному файлу заголовка. Второй — использовать #import с квалификатором импорта, чтобы библиотека типов источника событий записывалась в TLH-файл с сохранением сформированного атрибутом кода.

События, вызывающие срабатывание

Чтобы запустить событие, вызовите метод в интерфейсе, объявленном с помощью __event ключевого слова в классе источника событий. Если обработчики прикреплены к событию, они будут вызваны.

Код события COM

В следующем примере демонстрируется запуск события в COM-классе. Для компиляции и выполнения примера см. комментарии в коде.

// 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;

Затем сервер.

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

Затем клиент.

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

Выходные данные

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

События COM, зависимые от макета

Зависимость от макета становится проблемой только в программировании COM. При обработке собственных и управляемых событий сигнатуры (возвращаемый тип, соглашение о вызовах и аргументы) должны соответствовать своим событиям, но имена обработчиков не должны совпадать с их событиями.

Однако при обработке событий COM, если параметру присвоить значение layout_dependentevent_receivertrue , применяется имя и сопоставление подписи. Имена и подписи обработчиков в приемнике событий и в обработчиках событий должны точно совпадать.

Если параметр layout_dependent имеет значение false , соглашение о вызовах и класс хранения (виртуальный, статический и т. д.) могут быть смешанными и сопоставлены между методом события обработки и методами обработчика (его делегатами). Это немного более эффективно layout_dependent=true .

Например, предположим, что IEventSource определен как объект, имеющий следующие методы.

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

Предположим, источник событий имеет следующую форму:

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

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

Затем в приемнике событий любой обработчик, который прикрепляется к методу в IEventSource, должен соответствовать имени и подписи следующим образом.

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

См. также

Обработка событий