Gestion des événements dans COM
Dans la gestion des événements COM, vous configurez une source d’événement et un récepteur d’événements à l’aide des attributs et event_receiver
des attributs, respectivement, en spécifiantcom
type
= .event_source
Ces attributs injectent du code approprié pour les interfaces personnalisées, distribuées et doubles. Le code injecté permet aux classes attribuées de déclencher des événements et de gérer des événements via des points de connexion COM.
Remarque
Les attributs d’événement en C++ natif sont incompatibles avec C++ Standard. Ils ne sont pas compilés quand vous spécifiez le mode de conformité /permissive-
.
Déclaration d’événements
Dans une classe source d’événement, utilisez le __event
mot clé sur une déclaration d’interface pour déclarer les méthodes de cette interface en tant qu’événements. Les événements de cette interface sont déclenchés lorsqu'ils sont appelés comme méthodes d'interface. Les méthodes sur les interfaces d’événements peuvent avoir zéro ou plusieurs paramètres (qui doivent tous se trouver dans des paramètres). Le type de retour peut être void ou un type intégral.
Définition de gestionnaires d’événements
Vous définissez des gestionnaires d’événements dans une classe de récepteur d’événements. Les gestionnaires d’événements sont des méthodes avec des signatures (types de retour, conventions d’appel et arguments) qui correspondent à l’événement qu’ils géreront. Pour les événements COM, les conventions d’appel n’ont pas besoin de correspondre. Pour plus d’informations, consultez les événements COM dépendants de la disposition ci-dessous.
Raccordement de gestionnaires d’événements à des événements
En outre, dans une classe de récepteur d’événements, vous utilisez la fonction __hook
intrinsèque pour associer des événements à des gestionnaires d’événements et __unhook
dissocier les événements des gestionnaires d’événements. Vous pouvez raccorder plusieurs événements à un gestionnaire d'événements, ou plusieurs gestionnaires d'événements à un événement.
Remarque
En général, il existe deux techniques pour permettre à un récepteur d'événements COM d'accéder aux définitions de l'interface de source d'événements. La première, comme indiqué ci-dessous, consiste à partager un fichier d'en-tête commun. La deuxième consiste à utiliser #import avec le embedded_idl
qualificateur d’importation, afin que la bibliothèque de types source d’événement soit écrite dans le fichier .tlh avec le code généré par l’attribut conservé.
Déclenchement d’événements
Pour déclencher un événement, appelez une méthode dans l’interface déclarée avec le __event
mot clé dans la classe source d’événement. Si des gestionnaires ont été raccordés à l'événement, les gestionnaires sont appelés.
Code d’événement COM
L'exemple suivant montre comment déclencher un événement dans une classe COM. Pour compiler et exécuter l'exemple, consultez les commentaires du 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;
Puis le serveur :
// 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;
}
};
Puis le 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;
}
Sortie
MyHandler1 was called with value 123.
MyHandler2 was called with value 123.
Événements COM dépendants de la disposition
La dépendance aux dispositions représente un problème uniquement dans le cadre de la programmation COM. Dans la gestion des événements natifs et managés, les signatures (type de retour, convention d’appel et arguments) des gestionnaires doivent correspondre à leurs événements, mais les noms des gestionnaires n’ont pas à correspondre à leurs événements.
Toutefois, dans la gestion des événements COM, lorsque vous définissez le paramètre sur event_receiver
true
, le nom et la layout_dependent
correspondance de signature sont appliqués. Les noms et signatures des gestionnaires dans le récepteur d’événements et dans les événements connectés doivent correspondre exactement.
Lorsque layout_dependent
la valeur est définie false
, la convention d’appel et la classe de stockage (virtuelle, statique, etc.) peuvent être mixtes et mises en correspondance entre la méthode d’événement de déclenchement et les méthodes de raccordement (ses délégués). Il est légèrement plus efficace d’avoir layout_dependent
=true
.
Par exemple, supposons que IEventSource
est défini pour disposer des méthodes suivantes :
[id(1)] HRESULT MyEvent1([in] int value);
[id(2)] HRESULT MyEvent2([in] int value);
Supposez que la source d'événement a la forme suivante :
[coclass, event_source(com)]
class CSource : public IEventSource {
public:
__event __interface IEvents;
HRESULT FireEvent() {
MyEvent1(123);
MyEvent2(123);
return S_OK;
}
};
Ensuite, dans le récepteur d'événements, tout gestionnaire raccordé à une méthode dans IEventSource
doit correspondre au nom et à la signature, comme indiqué :
[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
}
};