Обработка событий в COM
В обработке событий COM вы настраиваете источник событий и приемник событий с помощью event_source
атрибутов соответственно, указываяtype
=com
.event_receiver
Эти атрибуты внедряют соответствующий код для пользовательских, диспетчеров и двух интерфейсов. Внедренный код позволяет классам атрибутов запускать события и обрабатывать события с помощью точек подключения COM.
Примечание.
Атрибуты событий в собственном языке C++ несовместимы со стандартом C++. Они не компилируются при указании /permissive-
режима соответствия.
Объявление событий
В исходном классе событий используйте __event
ключевое слово в объявлении интерфейса, чтобы объявить методы этого интерфейса как события. События этого интерфейса запускаются при вызове их в качестве методов интерфейса. Методы в интерфейсах событий могут иметь ноль или больше параметров (которые должны находиться в параметрах). Тип возвращаемого значения может быть пустым или любым целочисленным типом.
Определение обработчиков событий
Обработчики событий определяются в классе приемника событий. Обработчики событий — это методы с сигнатурами (возвращаемыми типами, соглашениями о вызовах и аргументами), которые соответствуют событию, которое они будут обрабатывать. Для событий COM соглашения о вызовах не должны соответствовать. Дополнительные сведения см. в разделе о событиях COM, зависящих от макета.
Перехватывание обработчиков событий к событиям
Кроме того, в классе приемника событий используется встроенная функция __hook
для связывания событий с обработчиками событий и __unhook
отключения событий от обработчиков событий. Можно прикрепить несколько событий к обработчику событий либо несколько обработчиков событий к одному событию.
Примечание.
Как правило, существует два метода предоставления приемнику событий COM доступа к определениям интерфейса исходного кода событий. Во-первых, как показано ниже, можно предоставить общий доступ к одному файлу заголовка. Второй — использовать #import с квалификатором embedded_idl
импорта, чтобы библиотека типов источников событий записывается в файл 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_dependent
параметра event_receiver
true
для параметра применяется сопоставление имен и подписей. Имена и подписи обработчиков в приемнике событий и событиях перехватчика должны точно совпадать.
Если 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
}
};
См. также
Обратная связь
https://aka.ms/ContentUserFeedback.
Ожидается в ближайшее время: в течение 2024 года мы постепенно откажемся от GitHub Issues как механизма обратной связи для контента и заменим его новой системой обратной связи. Дополнительные сведения см. в разделеОтправить и просмотреть отзыв по