How to sink events from a COM server.

By: mykle hoban

Abstract: This article describes how to respond to and handle events that are surfaced from a COM Automation server.

Topic

How to sink events from a COM server.

Explanation

Sinking events from a COM server can be horrible and tedious when done completely from scratch. This is why the VCL provides the TEventDispatcher template class. This class allows you to easily respond to events surfaced from a server.

Consider the following:
Your server does some data access (e.g. gets something from a web site). You need to notify the client that this has happened. In order to accomplish this, you've checked the "Generate event support code" box when creating your server. This is all well and good, you say to yourself, but now how do I respond to those events from my client??

The answer: TEventDispatcher! This handy little template class allows you to easily sink events from any server that will provide them. The rest of this article will be contained within commented sample-code. We will need to concern ourselves with our server's *Impl.cpp file (firing the events), and the client-side unit.cpp and unit.h files.

*Impl.cpp

STDMETHODIMP TTestEventsImpl::Trigger()
{
  //fire off our event
  Fire_Event1(WideString("Mesage message message message!"));
  return S_OK;
}



unit.h

//---------------------------------------------------------------------------

#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
class TForm1; //forward ref to TForm1

//here we derive from TEventDispatcher specialized on our class
//and the dispatch ID of the event interface
class EventHandler:
  public TEventDispatcher<EventHandler,&DIID_ITestEventsEvents>
{
private:
  bool connected;
  TForm1 *theform;
  ITestEventsPtr server;
protected:
  //overloaded from TEventDispatcher.
  HRESULT InvokeEvent(DISPID id, TVariant *params);

public:

  EventHandler();
  ~EventHandler();

  void Connect(TForm1 *form, ITestEventsPtr srv);
  void Disconnect();

  void __fastcall HandleEvent1(BSTR Message);

};
#endif



unit.cpp

//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"

//---------------------------------------------------------------------------

#pragma package(smart_init)

EventHandler::EventHandler()
{
  connected = false; //not connected;
  theform = false; //backptr to form is null
}

EventHandler::~EventHandler()
{
  if (connected)
    Disconnect();
}

/*
Here we do a switch on the DISPID (can be gotten from the type library).
params is an array of TVariant's. These we delegate to our event handlers.
*/
HRESULT EventHandler::InvokeEvent(DISPID id, TVariant *params)
{
  switch(id)
  {
    case 1:
        HandleEvent1(WideString(*params));
      break;
    default:
      ShowMessage("we shouldn't be here!");
  }
}

/*
This function attaches our event handler to a running instance of the server.
This is the method that actually sinks our events.
*/
void EventHandler::Connect(TForm1 *form, ITestEventsPtr srv)
{
  server = srv;
  theform = form; //back pointer to form to do stuff with it.
  server->AddRef(); //addref the server
  ConnectEvents(server); //helper function to connect us 
			//to the events interface
}

void EventHandler::Disconnect()
{
  DisconnectEvents(server);   //disconnect the events
  server->Release();          //release our referece
}

/* here we actually handle the event in any way we want */
void __fastcall EventHandler::HandleEvent1(BSTR Message)
{
  theform->Label1->Caption = WideString(Message);
}


Server Response from: ETNASC02