Share via


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écifiantcomtype= .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++ natifs sont incompatibles avec la norme C++. Ils ne sont pas compilés lorsque vous spécifiez le /permissive- mode de conformité.

Déclaration d’événements

Dans une classe source d’événements, utilisez la __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_receivertrue, 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
   }
};

Voir aussi

Gestion des événements