Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Handling COM Events in a Console Application, Part II

0.00/5 (No votes)
27 Jan 2003 1  
Simple code to deal with multiple event sinks/sources

The XYDispDriver class is a simple generic dispatch driver class that can be used to create almost any COM object and call its methods. In my previous article Handling COM Events in a Console Application, I talked about enhancements to this class that makes handling COM events easy. No need for messy template code, no event sink map, and no sink entry macro, etc. All we need to do is create a COM object implementing the event interface using our favourite tool and then call the Advise method of XYDispDriver.

The previous version of XYDispDriver does not deal with some complicated situations, such as multiple event sinks or multiple event sources. Some people may worry that this is not the right way to handle general COM events. After making some changes to the XYDispDriver class, I was able to write code that deals with these situations successfully. The resulting code, as you can see, is simpler than the code you would have written using ATL to accomplish the same thing.

The only significant change I made is adding an assignment operator to the XYDispDriver class. As described in my article Getting the most out of IDispatch, the CreateObject method of XYDispDriver will create a new instance of a COM object, query and store its type information for future use. Assigning one XYDispDriver object to another does not create a new instance of the corresponding COM object, instead it only copies the type information and the IDispatch pointer, and call the IDispatch::AddRef method to bump up the reference count. Therefore, the two XYDispDriver objects will be referring to the same COM object after the assignment.

Let's first see the code that deals with multiple event sinks. We will be using the same example as in the previous article: "TestCon" is an ActiveX control that fires the InvalidLoginData event when its Connect method fails and "TestHndlr" is another ActiveX control that implements the event interface of TestCon. What we need to do is "connect" two instances of TestHndlr to a single instance of TestCon so that when the InvalidLoginData event is fired, the event handling method will be invoked in both instances of TestHndlr. Here is the code of a console application that demonstrates the scenario of two event sinks.

#include <stdio.h>

#include "XYDispDriver.h"


void main()
{
    // declare XYDispDriver variables

    XYDispDriver dispCon1, dispCon2, dispEventA, dispEventB;
��� // create the TestCon control

��� if(dispCon1.CreateObject("TestCon.1"))
��� {
������� // make a copy (AddRef)

������� dispCon2 = dispCon1;
������� // create the first TestHndlr control

�� ���� if(dispEventA.CreateObject("TestHndlr.1"))
������� {
���������� // the class id of the event interface in the TestCon control

���������� CLSID clsidEventA = {0xe77a1f7e,0xe3ff,0x11d5,{0x88,0x12,0x00,0xb0,0xd0,0x55,0xb5,0x23}};
���������� // call the Advice method to set up the first event handler

 ����������dispCon1.Advise(dispEventA.GetDispatch(),clsidEventA);
������� }
  ����� else printf("Error: %x\n",dispEventA.GetLastError());
   �����// create the second TestHndlr control

   ���� if(dispEventB.CreateObject("TestHndlr.1"))
������� {
���������� // the class id of the event interface in the TestCon control

���������� CLSID clsidEventB = {0xe77a1f7e,0xe3ff,0x11d5,{0x88,0x12,0x00,0xb0,0xd0,0x55,0xb5,0x23}};
���������� // call the Advice method to set up the second event handler

  ���������dispCon2.Advise(dispEventB.GetDispatch(),clsidEventB);
������� }
������� else printf("Error: %x\n",dispEventB.GetLastError());
������� // call the Connect method, passing strUserName and strPassword parameters

������� // it should generate an InvalidLoginData event in either one of the following cases:

������� // 1. strUsername or strPassword is empty

������� // 2. the length of strPassword < 6

������� // 3. strPassword equals strUsername

������� dispCon1.InvokeMethod("Connect","MyName","MyPwd");
������� // you should see two event message boxes by now

��� }
��� else printf("Error: %x\n",dispCon1.GetLastError());
��� ::MessageBox(NULL,_T("Done"),_T("Test"),MB_OK); 
} 

The code that deals with multiple event sources is similar. Suppose the COM object TestCon has two different event interfaces, we can create two different types of COM objects TestHndlrA and TestHndlrB to implement these two interfaces, respectively. In the above code, if we let dispidEventA and dispidEventB be the dispids of these two event interfaces, and use the two XYDispDriver objects dispEventA and dispEventB to create these two event handler objects, then the above code will demonstrate the scenario of two event sources.

The zip file included with this article only has source code for the two ActiveX controls and the console app. You can download the source code for XYDispDriver from my article Getting the most out of IDispatch. You can also find more articles and software from my home page.

Disclaimer: I am not trying to show that ATL or any other related technology is not as good as my method. All I am doing is demonstrate a pretty simple and flexible (maybe unconventional) way to use COM objects.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here