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

Handling COM Events in a Console Application

0.00/5 (No votes)
26 Jan 2003 1  
Simple source code that handles COM events easily without the help of MFC, etc.

Introduction

The MFC class library makes handling ActiveX control events so easy that you are misled to believe that handling COM events is no big deal, until you try to do it within a console application. As you may have heard, I wrote the very simple XYDispDriver class, which can be used to create COM objects and call COM methods easily, especially in console applications. It would be nice if we can also use XYDispDriver to handle COM events, so I started to work on its enhancement.

Basically, a COM event is the opposite of COM method. Your code calls the methods in a COM object, and if you set up this thing called "event sink" correctly, the COM object will call your code (i.e. event handlers) when something happens within the object. That's why the COM event interface is called the "outgoing interface" (depending on which side you are standing, I guess).

Here is my idea. If I have a COM object which fires events, I will be able to find out the event interface, including its class ID and function signatures, from the type library. I will build a new COM object as my event handler, whose methods will match those in the event interface of the original COM object (same dispid, same signature, etc.). The magic is, I will use the XYDispDriver class to create both objects and somehow "connect" them together. Whenever the original object fires an event, the corresponding method in the new object will be called to handle the event. Of course, I have to make it easy to use (in console applications). You can add more methods to the event handler object, even passing function pointers to it so that you can call functions defined outside the object when handling events.

As you can see from the source code, I added an Advise method to the XYDispDriver class. This method takes two arguments, the first argument is the IDispatch pointer of the new COM object (the event handler), the second one is the class ID of the event interface of the original COM object. Calling the Advise method is all it takes to "connect" the event handler to the event interface. The Advise method is implemented with some boring COM API calls (QueryInterface, FindConnectionPoint, etc.), but the code is surprisingly simple. By the way, you don't have to bother with Unadvise (if you have heard of it), it is done automatically when the XYDispDriver object goes out of scope.

Now we test the idea. Using the MFC Control Wizard, I first created an ActiveX control TestCon, which has one method called Connect, this method will fire an InvalidLoginData event whenever it fails. Then I created a second ActiveX control TestHndlr as the event handler for the first control. The second control has one method that matches the dispid and signature of the InvalidLoginData event in the first control, the handler method will pop up a message box when it is invoked. Finally, I wrote a console application that uses these two controls. Here is the code of the console app.

#include <stdio.h> 

#include "XYDispDriver.h"


void main() 
{ 
    // declare two XYDispDriver variables 

    XYDispDriver dispCon, dispEvent; 
    // create the TestCon control 

    if(dispCon.CreateObject("TestCon.1")) 
    { 
        // create the TestHndlr control 

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

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

            dispCon.Advise(dispEvent.GetDispatch(),clsidEvent); 
        } 
        else printf("Error: %x\n",dispEvent.GetLastError()); 
        // call the Connect method, passing "username"

        // and "password" parameters 

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

        // 1. "username" or "password" is empty 

        // 2. "password" length < 6 

        // 3. "password" equals "username" 

        dispCon.InvokeMethod("Connect","MyName","MyPwd"); 
        // you should see the event message box by now 

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

You can use the same technique with a more complicated event interface, of course. I have tested my XYMessenger.OCX control with this method, so it can now be used even in console applications. The more complicated situations (such as multiple event sinks, etc.) will be dealt with in a separate article.

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 other article. You can also find other articles and software from my home page.

Disclaimer

I do not claim that I invented anything new here. I am not a COM expert, this is actually the first time I used the IConnectionPoint interface. There could be other more "standard" ways to do the same thing. Thank you.

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