In depth analysis of fire events in C++ programs and event handling in COM

  • 2020-05-07 20:13:01
  • OfStack

Event handling in native C++
When
handles native C ++ events, you set the event source and event receiver using the event_source and event_receiver features, respectively, and specify type=native. These features allow the classes to apply them to fire and handle events in the native non-COM context.
The statement event
In the event source class, a method declaration can be declared as an event using the s/s keyword. Be sure to declare the method, but do not define it; Doing so causes a compiler error because the compiler implicitly defines the method when it is converted to an event. A native event can be a method with zero or more parameters. The return type can be void or any integer.
Define an event handler
In the event sink class, you can define event handlers that are methods with signatures (return types, call conventions, and parameters) that match the events they will handle.
Hook an event handler to an event
Also in the event receiver class, you can use the inner function s to associate the event with the event handler and s to cancel the association of the event with the event handler with s s s. You can hook multiple events to one event handler, or hook multiple event handlers to one event.
Trigger events
To fire an event, simply call a method declared as an event in the event source class. If the handler is already hooked to an event, the handler is invoked.
Native C++ event code
The following example shows how to fire an event in native C++. To compile and run this example, refer to the comments in the code.
The sample code


// evh_native.cpp
#include <stdio.h>

[event_source(native)]
class CSource {
public:
  __event void MyEvent(int nValue);
};

[event_receiver(native)]
class CReceiver {
public:
  void MyHandler1(int nValue) {
   printf_s("MyHandler1 was called with value %d.\n", nValue);
  }

  void MyHandler2(int nValue) {
   printf_s("MyHandler2 was called with value %d.\n", nValue);
  }

  void hookEvent(CSource* pSource) {
   __hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
   __hook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
  }

  void unhookEvent(CSource* pSource) {
   __unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler1);
   __unhook(&CSource::MyEvent, pSource, &CReceiver::MyHandler2);
  }
};

int main() {
  CSource source;
  CReceiver receiver;

  receiver.hookEvent(&source);
  __raise source.MyEvent(123);
  receiver.unhookEvent(&source);
}

Output:


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

Event handling in COM
in COM event handling, you use the event_source and event_receiver features to set the event source and event receiver, respectively, and specify type=com. These features inject code for custom interfaces, scheduling interfaces, and dual interfaces so that the classes to which these interfaces can be applied fire events and process them through COM join points.
The statement event
In the event source class, use the s/s 52en keyword on the interface declaration to declare the methods of the interface as events. When you invoke the events of that interface as interface methods, they are fired. Methods on the event interface can have zero or more arguments (all in arguments). The return type can be void or any integer.
Define an event handler
In the event sink class, you can define event handlers that are methods with signatures (return types, call conventions, and parameters) that match the events they will handle. For COM events, the calling convention does not have to match; For more information, see the layout dependent COM event below.
Hook an event handler to an event
Also in the event receiver class, you can use the internal function s to associate the event with the event handler and s to cancel the association of the event with the event handler with s. You can hook multiple events to one event handler, or hook multiple event handlers to one event.
Pay attention to
In general, there are two ways to enable an COM event receiver to access the event source interface definition. The first is to share common header files, as shown below. The second is to use #import in conjunction with the embedded_idl import qualifier to write the event source type library to the.tlh file that preserves the feature-generated code.
Trigger events
To fire an event, simply call the method in the interface declared with the event source class using the s 72en keyword. If the handler is already hooked to an event, the handler is invoked.
COM event code
The following example shows how to fire an event in an COM class. To compile and run this example, refer to the comments in the 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;

Then there's the 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;
  }
};

And then the client side:


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

The output


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

depends on the COM event of the layout
The
layout dependency is just one problem in COM programming. In both native and managed event processing, the handler's signature (return type, invocation convention, and parameters) must match its event, but the handler's name does not have to match its event.
However, in COM event processing, if the layout_dependent parameter of event_receiver is set to true, the name and signature are forced to match. This means that the name and signature of the handler in the event receiver must exactly match the name and signature of the event to which the handler will hook.
When layout_dependent is set to false, the calling conventions and storage classes (virtual, static, and so on) between the fire event method and the hook method (whose delegate) can be mixed and matched. Setting layout_dependent to true is a bit more efficient.
For example, suppose IEventSource is defined as having the following methods:


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

Assume that the event source has the following form:


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

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

Then in the event receiver, any handler that hooks into the method in IEventSource must match its name and signature, as shown below:


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


Related articles: