Change of Article Title
Please note that I have changed the title of this article from "TEventHandler - A C++ COM Event Handler For IDispatch-Based Events" to the current title "Understanding COM Event Handling." The latter, I believe, is a better title which gives a more accurate picture of the intended theme of this article, i.e. to expound carefully the internal mechanisms behind COM event handling.
If you have ever done any development work involving the use of COM objects, chances are you would have encountered the need for COM object event handling. Visual Basic users will know how simple it is to connect with the event interface(s) of the COM (or ActiveX) objects. The VB IDE lays out the event handling function codes nicely for the user. All the user has to do is to fill in the details of the event handling functions.
For Visual C++ users, this is not always so straight forward. If your COM object happens to be an ActiveX control and you are using MFC, then yes, the Visual C++ IDE provides Wizards that can help you generate event handler function stubs. All the necessary codes (e.g. inserting the event sink map and event entry macros) are done automatically for you.
But what if your COM object is not an ActiveX control? What if you are using straight COM objects which also fire events and you need to handle those events?
If you are an MFC user, you may want to tinkle with the various MFC macros to see if you can fit in an event handler function into your code either manually or via the Wizard. I personally believe this is possible. But you need to be armed with an intimate knowledge of MFC and its generated macros.
If you do not use MFC, you may want to experiment with ATL codes (e.g.
SINK_ENTRY_EX, etc.) to perform event handling. The ATL macro codes are certainly not simple but they are well-documented in MSDN and they do provide standard handling mechanisms.
In this article, I will go back to basics and seek to explain the fundamental principles of how event handling is done in COM. I will also provide a C+ class which serves as a basic and simple (at least in terms of code overhead) facilitator for COM Object Event Handling.
I do this via a special custom-developed template class named
TEventHandler which I have used in many projects. This class uses COM first principles and primitives and avoids the use of complicated macros. The sections following expound this class in detail. I assume that the reader is suitably conversant with C++, ATL and the concepts of template classes. However, before we start discussing the
TEventHandler class, let us explore the fundamental principles of Event Handling in COM.
Event Handling in COM
When we develop our usual COM objects, we provide implementations for interfaces (defined in an IDL file) which we write ourselves or have been supplied to us. Such implementations facilitate what are known as "incoming" interfaces. By "incoming," we imply that the object "listens" to its client. That is, the client calls methods of the interface and, in this way, "talks" to the object.
Referring to the diagram below, we can say that
ISomeInterface is an "incoming" interface provided by the COM object on the right.
As Kraig Brockschmidt puts it so well in his book "Inside OLE," many COM objects themselves have useful things to say to their clients. And clients may want to listen to COM objects too. If such a two-way dialog is desired, something known as an "outgoing" interface is required. The term "outgoing" is used in the context of the COM object. It is outgoing in the perspective of the COM object. Imagine a situation in which the role of "talker" and "listener" is reversed as shown in the diagram below:
Referring to the diagram above, we can say that
ITalkBackInterface is an "outgoing" interface supported by the COM object on the right. The COM object invokes the methods of
ITalkBackInterface and it is the Client that implements the
ITalkBackInterface methods. A COM object that supports one or more outgoing interfaces is known as a Connectable Object or a Source. A connectable object can support as many outgoing interfaces as it likes. Each method of the outgoing interface represents a single event or request. All COM objects, regardless of whether they are non-visual COM objects or ActiveX controls (generated manually or via MFC or ATL), use the same mechanism (connectability) for firing events to their clients.
Events and Requests
Events are used to tell a client that something of interest has occurred in the object - a property has changed or the user has clicked a button. Events are particularly important for COM controls. Events are fired by COM objects and no response from the client is expected. In other words, they are simple notifications. Requests, on the other hand, is how a COM object asks the client a question and expects a response in return. Events and requests are similar to Windows messages, some of which inform a window of an event (e.g.
WM_MOVE) and some will ask for information from the window (e.g.
In both cases, the client of the COM object must listen to what the object has to say and then use that information appropriately. It is the client, therefore, that implements the outgoing interfaces which are also known as sinks (I really dislike this name but it has become common and ubiquitous in the world of COM and .NET). From a sink's perspective, this outgoing interface is actually incoming. The sink listens to the COM object through it. In this context, the connectable COM object plays the role of a client.
How Things are Tied Up Together
Let us take a helicopter view of the entire communications situation. There are three participants:
- The COM object itself
- The Client of the COM object
- The Sink
The Client communicates with the COM object as usual via the object's incoming interface(s). In order for the COM object to communicate back to the client in the other direction, the COM object must somehow obtain a pointer to an outgoing interface implemented somewhere in the client. Through this pointer, the COM object will send events and requests to the client. This somewhere is the Sink. Let us illustrate the above with a simple diagram:
Note that the Sink is an object by itself. The Sink provides the implementation for one or more outgoing interfaces. It is also usually strongly tied to the other parts of the client's code because the whole idea of implementing a sink is for the client code to be able to react to an event and/or to respond to some request from the COM object.
How Does a COM Object Connect to a Sink?
But how does a COM object connect to a Sink in the first place? This is where the notion of Connection Points and Connection Point Containers come in. We will explore this in detail in the next section.
Connection Points and Connection Point Containers
For each outgoing interface that a COM object supports (note the use of the word "support;" the COM object itself does not implement this interface, but rather, invokes it), the COM object exposes a small object called a connection point. This connection point object implements the
It is through this
IConnectionPoint interface that the client passes its Sink's outgoing interface implementation to the COM object. Reference counts of these
IConnectionPoint objects are kept by both the client and the COM object itself to ensure the lifespan of the two-way communications.
The method to call (from the client side) to establish event communication with the COM object is the
IConnectionPoint::Advise() method. The converse of
IConnectionPoint::Unadvise() which terminates a connection. Please refer to MSDN documentation for more details of these methods.
IConnectionPoint interface method calls, a client can start listening to a set of events from the COM object. Note also that because a COM object maintains a separate
IConnectionPoint interface for every outgoing interface it supports, a client must be able to use the correct connection point object for every sink it implements.
How then, does the Client choose the appropriate connection point for a sink? In comes Connection Point Containers. An object which is connectable must also be a Connection Point Container. That is, it must implement the
IConnectionPointContainer interface. Through this interface, a Client requests for the appropriate Connection Point object of an outgoing interface.
When a client wants to connect a Sink to a Connection Point, it asks the Connection Point Container for the Connection Point object for the outgoing interface implemented by that Sink. When it receives the appropriate connection point object, the Client passes the Sink's interface pointer to that connection point. The
IConnectionPointContainer interface pointer itself can be obtained easily via
QueryInterface() on the COM object itself. Nothing speaks better than an example code which is listed below:
void Sink::SetupConnectionPoint(ISomeInterface* pISomeInterface)
IConnectionPointContainer* pIConnectionPointContainerTemp = NULL;
IUnknown* pIUnknown = NULL;
this -> QueryInterface(IID_IUnknown, (void**)&pIUnknown);
pISomeInterface -> QueryInterface (IID_IConnectionPointContainer,
pIConnectionPointContainerTemp -> Release();
pIConnectionPointContainerTemp = NULL;
m_pIConnectionPoint -> Advise(pIUnknown, &m_dwEventCookie);
pIUnknown -> Release();
pIUnknown = NULL;
The sample function above describes the
SetupConnectionPoint() method of a
Sink class. The
Sink class implements the methods of the outgoing interface
SetupConnectionPoint() method takes a parameter which is pointer to an interface named
ISomeInterface. The COM object behind
ISomeInterface is assumed to be a Connection Point Container. The following is an outline of the function's logic:
- We first
Sink object itself for its
IUnknown interface pointer. This
IUnknown pointer will be used later in the call to
- Having successfully obtained the
IUnknown pointer, we next
QueryInterface() the object behind
pISomeInterface for its
- Having successfully obtained the
IConnectionPointContainer interface pointer, we use it to find the appropriate Connection Point object for the outgoing interface
- If we are able to obtain this Connection Point object (it will be represented by
m_pIConnectionPoint), we will proceed to call its
- From here onwards, whenever the COM object behind
pISomeInterface fires an event to the sink (by calling one of the methods of
ISomeEventInterface), the corresponding method implementation in the
Sink object will be invoked.
I certainly hope that the above introductory sections on Event Handling in COM, Connection Points and Connection Point Containers will have served to provide the reader with a clear understanding of the basics of event handling. It is worth re-mentioning that the above principles are used whatever the type of COM object is involved (e.g. straight COM object, ActiveX controls, etc). With the basics explained thoroughly, we shall proceed to expound on the sample source codes, especially the
TEventHandler template class.
The Sample Source Code
I will attempt to explain
TEventHandler by running through some example codes. Please refer to the source files which are contained in the TEventHandler_src.zip file. In the set of sample codes, I have provided two sets of projects:
The EventFiringObject project contains the code for a simple COM object which implements an interface named
IEventFiringObject. This COM object is also a Connection Point Container which recognizes the
_IEventFiringObjectEvents connection point. The relevant IDL constructs for this COM object is listed below for discussion purposes:
interface IEventFiringObject : IDispatch
[id(1), helpstring("method TestFunction")]
HRESULT TestFunction([in] long lValue);
[id(1), helpstring("method Event1")] HRESULT Event1([in] long lValue);
[default] interface IEventFiringObject;
[default, source] dispinterface _IEventFiringObjectEvents;
In the EventFiringObject project, we implement a C++ ATL class named
CEventFiringObject which implements the specifications of
CEventFiringObject provides a simple implementation of the
TestFunction() method. It simply fires
Event1 which is specified in
STDMETHODIMP CEventFiringObject::TestFunction(long lValue)
TestClient is a simple test application: an MFC dialog-based application which instantiates the
EventFiringObject COM object. It also attempts to handle the
Event1 event fired from
EventFiringObject. I will walk through the TestClient code more thoroughly to explain the process of event handling. The client code centers around the
CTestClientDlg class which is derived from
CDialog. In TestClientDlg.h, notice that we declare an instance of a smart pointer object which will be tied to the COM object which implements
Then, in the
CTestClientDlg::OnInitDialog() function, we instantiate
We also create a button in our simple dialog box labeled "Call Test Function." In the click handler for this button, we invoke the
TestFunction() of our
m_spIEventFiringObject -> TestFunction(456);
Thus far, we have dealt with mostly typical COM client code. The fun begins when we invoke
TestFunction(). We know that
TestFunction() will cause the
m_spIEventFiringObject COM object to fire the
Event1 event. This is where the real action starts.
The TEventHandler Class
General Design Goals and Example Use Case
TEventHandler class is supplied in TEventHandler.h. It works according to the following design:
- It serves the role of a Sink for a client.
- It generically handles one event interface which must be dispinterface-based (i.e. derived from
- After receiving an event fired from the COM object,
TEventHandler will call a predefined method of its client. This is how
TEventHandler clients get notified of events fired from the COM object.
The predefined method of the client must be defined using the following signature:
typedef HRESULT (event_handler_class::*parent_on_invoke)
Notice that the predefined method's parameters match those of
IDispatch::Invoke() except for the addition of a parameter (first in the list) which is a pointer to the
TEventHandler instance itself. This parameter is supplied as it may be useful to client code. In the context of our example code, our usage of
TEventHandler can be represented by the following diagram:
What TEventHandler is Not Designed to Do
Why do we make an apparent rehash of the
IDispatch::Invoke() method? What value-add could
TEventHandler have if your callback function still has to handle all the parameters of the
The answer is that the
TEventHandler class is not primarily designed to simplify the handling of the parameters of the event methods (although this might be possible, see my comments on this later in this section).
It is designed to be a sink object. It is meant to readily make available (for a client) a sink which can be hooked up to receive the dispinterface-based event of a COM object. And then have the sink automatically call the mirror
Invoke() method of the client.
Note that using C++ templates alone, it will not be possible to anticipate in advance the return values and parameter lists of event methods. This would require the work of Wizards which can read all these information from the type libraries associated with the connectable COM objects and then generating function codes which match the signatures of event methods.
TEventHandler is not a wizard. It is a C++ template (which makes it sort of a bona-fide code generator, but it can't do everything, e.g. read a type library and generate code according to information found therein...). It is also meant to be a Sink for one event interface which must be derived from
IDispatch, and hence
IDispatch (see the next section "Why Is TEventHandler Dispinterface-based?" for more information on the reasons behind this).
Why is TEventHandler Dispinterface-based?
In order for
TEventHandler to be a Sink for
ISomeEventInterface, it must be derived from
ISomeEventInterface and it must implement the methods of that interface. I have chosen to make
TEventHandler derive from
IDispatch, thereby making it a Sink for an event interface that also derives from
Interfaces that derive from
IDispatch are also known as dispinterfaces (for dispatch interfaces). Interfaces, in general (not just event interfaces), that are dispinterfaces are very common. They are common because of the long-time need for COM to support Visual Basic.
IDispatch interface is the basis behind Visual Basic's achievement of something known as late binding which means the act of programmatically constructing a function call (together with the inclusion of parameters and the receipt of return values) at runtime. It is perhaps the most generic and flexible of all COM interfaces.
Take note that I'm using the term late binding in a generic way (I'm not referring to the C++ concept of vtables which is another implementation of late binding).
IDispatch can be used to define a virtual interface the methods of which are called via the
IDispatch::Invoke() method. This system of runtime function invocation is known as automation (formerly OLE-automation).
To distinguish one
IDispatch virtual interface from another, something known as a Dispatch Interface ID is used (DIID). This is programmatically no different from a normal Interface ID (IID). Take a look at the following code fragment:
pIConnectionPointContainerTemp -> FindConnectionPoint
Here, we are asking a connection point container to return to us a connection point that supports the outgoing interface that is identified by the DIID which is equivalent to the result of
Visual Basic applications can only handle ActiveX object events through sinks which are dispinterface-based. This is natural both because of the fact that Visual Basic cannot handle non-dispinterface-based events, and also because of the need to handle events fired from any and all ActiveX objects.
The central point behind this is that while event interfaces need not be dispinterface-based (their methods can be of any signature, the only mandate is that the event interface must also derive from
IUnknown), Visual Basic is not able to internally anticipate the design of these custom event interfaces and to generate Sinks for them.
Furthermore, the types of method return values and parameters must be confined to those that Visual Basic is able to understand and internally process. Hence, the only way to standardize the handling of event interfaces is to require that they be derived from
IDispatch, and that the return and parameter types be from a wide but limited ranged set. This set of types is known as the automation-compatible-types.
Like Visual Basic, it is not possible for us to design the
TEventHandler template class to be a Sink for custom event interfaces. Remember that we must actually implement the methods of the event interface that
TEventHandler is to be a Sink for. Hence, I decided that
TEventHandler should handle only dispinterface-based events. And there are plenty of COM object sources which support them.
TEventHandler template class is defined (in summary) as follows:
template <class event_handler_class, typename device_interface,
class TEventHandler : IDispatch
TEventHandler is derived from
IDispatch. However, note, it need not implement all of the methods of
IDispatch. Only the basic
QueryInterface() and the
Invoke() methods are required at minimum.
TEventHandler takes in three template parameters which are:
event_handler_class parameter indicates to
TEventHandler the name of the class which will contain the predefined method to be invoked when the COM object fires an event of the outgoing interface. In out example use case, this parameter will be the
CTestClientDlg class. The
CTestClientDlg class will contain the invocation function:
It is in this function that
CTestClientDlg will handle events fired from the
EventFiringObject COM object. The
device_interface parameter refers to the interface type of the COM object (whose event we are trying to receive). In our example use case, this will be
The last template parameter is
device_event_interface and this indicates the interface type of the outgoing interface supported by the COM object. This will be the interface that must be implemented by the Sink object. In our example use case, this will be
_IEventFiringObjectEvents. And note that because
_IEventFiringObjectEvents is essentially derived from
IDispatch, our Sink object (which is
TEventHandler) is also derived from
All the above template parameters are used in order that the VC++ compiler be able to generate a C++ class which has been tailored to contain methods, properties and parameter types which match those of
_IEventFiringObjectEvents. Hence, a customized class will eventually be created for further use in the code.
Usage of the
TEventHandler class is simple and straightforward. Let us walk through some example codes from TestClient:
- We need to define a specific class type based on
using the TEventHandler template. *****
typedef TEventHandler<CTestClientDlg, IEventFiringObject,
Note that we are not instantiating an object here! We are merely defining a C++ class via the use of
TEventHandler. We will call this new C++ class
IEventFiringObjectEventHandler is the customized class that we mentioned earlier. This
IEventFiringObjectEventHandler is the Sink for the outgoing interface
_IEventFiringObjectEvents supported by the
EventFiringObject COM object.
- In our
CTestClientDlg class, we will now define a pointer to an instance of the
- We need to define the invoke method which will be called by the
IEventFiringObjectEventHandler class object when the
EventFiringObject fires an event based on the
_IEventFiringObjectEvents outgoing interface. We have seen this method before and it is defined as:
Please note that the reader must be familiar with the
IDispatch::Invoke() method in order to be able to interpret the values contained in the various parameters of this method. See the example code in
OnEventFiringObjectInvoke() itself and refer to MSDN documentation for further details.
- In the
CTestClientDlg::OnInitDialog() function, we instantiate
m_pIEventFiringObjectEventHandler = new IEventFiringObjectEventHandler
Here, we instantiate with constructor parameters according to those defined for
TEventHandler. The first parameter is a reference to the
CTestClientDlg object. The second is the smart pointer object
m_spIEventFiringObject. This will be cast to an
IEventFiringObject interface pointer and so the inner pointer contained inside
m_spIEventFiringObject will be supplied to the constructor.
The last parameter is a pointer to a method of the
CTEstClientDlg class which conforms to the
parent_on_invoke method signature. You will note in the
TEventHandler constructor that once an instance of
IEventFiringObjectEventHandler is created, the
SetupConnectionPoint() method is called. This method will duly perform all the required connection point protocols to establish event connectivity with the
EventFiringObject COM object.
- When we no longer want to maintain event connection with the
EventFiringObject COM object, we shutdown the connection point as in the
m_pIEventFiringObjectEventHandler -> ShutdownConnectionPoint();
m_pIEventFiringObjectEventHandler -> Release();
m_pIEventFiringObjectEventHandler = NULL;
- To invoke the event handling code, I have included a button in the
CTestClientDlg dialog box, and the handler to this button will call the
TestFunction() method which will internally fire
Event1. This will lead to
CTestClientDlg::OnEventFiringObjectInvoke() being called by the
IEventFiringObjectEventHandler class object:
if (dispidMember == 0x01)
long lValue = 0;
varlValue = (pdispparams -> rgvarg);
lValue = V_I4(&varlValue);
sprintf (szMessage, "Event 1 is fired with value : %d.", lValue);
::MessageBox (NULL, szMessage, "Event", MB_OK);
I certainly do hope you will find the
TEventHandler class useful. C++ templates are really superb in generating generic code. There is already talk of C# providing template features. I really can't wait to get my hands on this. If you have any comments on
TEventHandler, on how to improve it further, please drop me an email anytime.