Connection Points and Asynchronous Calls - Part II

Introduction

This article is in continuance with my earlier article which discribed the need for a connection point.It however showed a simulation of connection points.

Why this article?

In the previous article I explained and demonstrated the need of a connection point.I developed a simple server whihc has a custom callback interface and which is being called by the clients.That was a kind of "simulation" of the Connection Points.In this tutorial I am going to show how to create a server with an outgoing interface in ATL and how to implement a sink in both ATL and MFC.

What is needed?

You will need ATL 3.0 WindowsNT(4.0 or later ) or Windows 98.VC++(6.0)

What is a connection points?

A connection point is a spot where an object calls back its.An object which has a capability to call back its clients is called connectable object or a container..And for an object to be a Connectable object the object should implement IConnectionPointContainer interface.For a client to know whether the given object supports any connection points ,it needs to queryinterface for this(IConnectionPointContainer)interface.Now a connectable object can support one or more connection points(outgoing interfaces).Each such connection point needs to support IConnectionPoint interface.Now each outgoing interface (which implements IConnectioPoint) needs to be implemented by a client to get the events back.If you look at the IConnectionPointContainer interface you will find it has two methods (besides the IUnknown methods) called EnumConnectionPoints and FindConnectionPoint .First one gives you a list of all the connection points supported in the container and second one actually takes a GUID and return you a pointer to IConnectioPoint.Using that IConnectionPoint pointer you cann do some good things .Suppose you are a client and you want to get events back from the server and for that purpose you will implement the outgoing interface published by the server.This implementation of the outgoing interface is called Sink.Once you implemented this sink you need to tell server about this.You do it by calling Advise method on IConnectionPoint interface.Since server can have many other clients,it will identify your sink by giving you back a cookie.Later when you want to break the connection you will use this cookie and call UnAdvise method on the server.

Enough of theory to get started.

Tutorial!

First of all run extract the code from the Code.zip. You will get these two directories : MFCSinkApp and ServerWCP .Next run the Visual C++ and open the ServerWCP.dsw .You will find two projects in the workspace ServerWCP.dsp and MFCSinkApp.dsp.Compile the Server first(choose Debug configuration ) Compile the MFCSinkApp (also Debug).

Once you built both the projects successfully,Press Ctrl-F5 (to run the application)and you will get following screen

Fig:ConnectionPointsTwo_1.jpg

Press "Start Timer "and after few seconds press "Start Calculation" and you should see something like this.

Fig:ConnectionPointsTwo_2.jpg

Now its time to explain what is all this !We will do a step by step tutorial explaining each step and get to this simple application.But first let me tell you what just happened.When you pressed "Call Server" from the menu client called an interface method AddNumbers(short a ,short b) on timer event passing different numbers every time.Server buffers up the data that was sent every time AddNumbers() was called.When you pressed "Start Calculation" ,the Server spawns a background thread and starts "calculations"( well,l just adds the numbers passed to each call to AddNumbers()) and when done fires events back to the Client.Client simply displays the results in the list box.

 

We will now do everything step by step.

First of all if you have compiled the Server code before ,unregister the dll by typing the following command in the command line regsvr32 -u \ServerWCP\ServerWCP.dll.(specify the path where this dll is located).You should see something like this

Fig:ConnectionPointsTwo_3.jpg

First of all , start a seperate instance of Visual C++ and create a new project called ServerWCP.Choose ATL COM AppWizard

Fig:ConnectionPointsTwo_4.gif

Now select the DLL as the Server Type and press finish.Acknowledge the Information box.Insert an ATL object from the Insert menu. Call this object CoCalculator and from the attributes tab check "Support Connection Points" Select Free for Threading model ,Custom for Interface Type and say No to aggregation.

Fig:ConnectionPointsTwo_5.jpg

Now Press OK .

Open the Class View tab from the Workspace .You will see the following

Fig:ConnectionPointsTwo_6.jpg

Note that there is one ICoCalculator interface and a class CCoCalculator. But what is _ICoCalculatorEvents. This is the result of checking the option "Support Connection Points" from the attributes tab.If you double click the _ICoCalculatorEvents you will see the IDL file which will tell you that this is a dispinterface(IDispatch).It has no methods and no properties as of now.If you look at the coclass defintin CoCalculator you will see this


coclass CoCalculator
{ [default] interface ICoCalculator; [default, source] dispinterface _ICoCalculatorEvents; };
There are two interfaces which are being exposed by the cocalculator object.One is ICoCalculator and another is _ICoCalculatorEvents.Note both are having default attribute but the second one (_ICoCalculatorEvents) has one more attribute called source This attribute indicates that the object is a source of outgoing interface and that a client can implement this interface to sink the events.Things will become more clear in a while.

Lets now add some methods to our server.Right click on the ICoCalculator interface in the class view and select Add Method from the pop up menu .Add the method called AddNumbers and in the paramter box type two shorts as

Fig:ConnectionPointsTwo_7.jpg

Press OK.

Similarly add other methods called ReleaseObj,BeginTask and GetResults .None of these take any paramters So you leave the Parameters edit box blank in each case.

So far so good.Now what next? We will want to fire an event back to client when we finish task (of adding numbers).For that purpose,we will define a method called AdditionDone() which has one [in] short paramter called lResult.Whenever we want to fire event we will call this method on the _ICoCalculatorEvents interface which is implemented by the client.

So ,go to class view again and right click the _ICoCalculatorEvents interface and choose Add Method from the popup menu.And then type in the name as AdditionDone and paramters as [in] short Result. Remember to choose void from the return type(Default is HRESULT Why do we need to choose void will be explained later).

Press OK.Open IDL file and you can see that there is a new method included in the _ICoCalculatorEvents interface defnition


[id(1), helpstring("method AdditionDone")] 
void AdditionDone([in] short Result);
id(1) indicates that the interface method has a disp ID of 1.

Now build the Debug version of the project..Once you compile the project successfully,goto the class View again and right click on CCoCalculator.You will see a number of menu items Select "Implement Connection Points" and you will see the following dialog box

Fig:ConnectionPointsTwo_8.jpg

As you can see there is one interface in interfaces list box called _ICoCalculatorEvents.Remember when we checked the "Support Connection Point" check box ,we had got this interface generated by the ATL Wizard. Well what actually happened is that we asked ATL Wizard that we want to have an outgoing interface and ATL Wizard generated this interface definition for us.That is our source or outgoing interface(which will be going out for someone to be implemented).Now when we are saying that Implement Connection Point, we are presented with a list of all the outgoing interfaces.SInce we only have one that is what we see in the Interfaces list box.Check on the _ICoCalculatorEvents and press OK.You must have noticed that there is one more button called Add Typelib .If you click this button you will be presented with all the registered typelibs and if you choose one there will be one more tab added .For now we are not going to do anything like that.

Now that you pressed OK you will see a change in the Class View.You will find that there is one more Class added called CProxy_ICoCalculatorEvents .Double click on this and you will see the definition.This is a template class derived from IConnectionPointImpl.This is the ATL implementation class for IConnectionPoint(In case you forgot what was IConnectionPoint you may want to scroll a little up to revise the discussion).

The IConnectioPointImpl is a template class which has 3 parameters.The second one is the GUID of the outgoing interface which is DIID__ICoCalculatorEvents in our case ,the third one indicates the class which will manage the connection points.If you have read my previous article we used CComDynamicUnkArray to store the IUnknown pointers of all connections.

The class CProxy_ICoCalculatorEvents has one method called Fire_AdditionDone.Remember we had added AdditionDone as the method on our outgong interface.The wizard has added the word Fire_ in front of it.If you read the method carefully you will find that there is a for loopwhich firesevents(actually invokes the method with disp ID 0x1(which is the disp ID of AddtionDone method) for all connections.Lets say our Server has 4 connections or clients.This loop will invoke AddtionDone on each clients implementation of our outgoing interface.At this stage it is worth mentioning how server knows what clients are connected to it.Answer is that Clients Advise the server of their sinks and server maintains these connections in a vector called m_vec.m_vec is of type CDV the third paramter to the IConnectionPoint template class.In my previous article we had maintained all these things ourseleves but here everything is maintained by the ATL .hidden from you.Defintion of IConnectionPointImpl is in atlcom.h You may wish to open that file and take a look.

Ok now we will add some code to our ICoCalculator's AddNumbers method.

You can directly cut and paste the code from the source files included.What we are doing here is whenever a client calls AddNumbers we store the paramters passed in a struct called ParamStruct and push that Struct in a global vector called gParamQ.Here is the defintion of ParamStruct and gParamQ.


typedef std::vector<ParamStruct> PARAMQ;
PARAMQ  gParamQ;
struct ParamStruct
{
	short FParam;
	short SParam;
	short lResult;
};

Now when the client wants us to process the data sent so far it calls the method BeginTask .BeginTask starts a worker thread which performs background task.It sets the event called DataReady whenever it finishes processing (actually adding )all the pairs of the data sent so far.All the results are stored ina vector of shorts called gResultQ I will not go into the details of threads and events .Once done it calls a SendResults method which fires events to the client.What sendresults does is that it calls Fire_AddtionDone of the CProxy_ICoCalculatorEvents (derived from IConnectionPointImpl).In the Fire_AdditionDone we already saw how the method(with disp ID 1) is being invoked for all clients.And all the sink interfaces are in m_vec array.I hope it is clear till now.Compile the code and you may get some errors like variable not declared.Simply make sure you pasted both CoCalculator.cpp and .h correctly.You will still get one warning about C++ exception handling .You can get rid of this warning by enabling C++ exception handling in the C++ tab under category C++ language.

Okay Now time for a client,What we are going to do is that we are going to make a client in MFC .Earlier I thought we will go step by step creating the MFC Client then I realized that since we are doing ATL tutorial that won't make any sense.

So just inset into the current workspace the MFCSinkApp.dsp which you downloaded earlier here.Copy the whole directory to the directory where you created the ServerWCP .Now go to the Class View you can see _ICoCalculatorEvents class under the MFCSinkApp classes.How we got that class? Actually we imported a class from the ServerWCP.tlb.This class is derived from COleDispatchDriver which is a kind of MFC wrapper class around IDispatch.

Open the MainFrame.cpp.See the Constructor.


CMainFrame::CMainFrame()
{
pConnect= NULL;
 //Initialize COM  libraries...create MTA...
 CoInitializeEx(NULL,COINIT_MULTITHREADED);
 EnableAutomation();	

 //creat the instance of the calculator server...
 pConnect.CreateInstance(__uuidof(CoCalculator));

 //advise the server that this (mainframe) is the sink for events...
 BOOL Ret = AfxConnectionAdvise( 
  pConnect,
  DIID__ICoCalculatorEvents  , 
  GetIDispatch(FALSE), //get the IDispatch assocaiated with Mainframe...
  FALSE, //donod addref
  &m_dwCookie );//cookie to break connection later...
}
One method of interest is AfxConnectionAdvise() which is used to advise the server of the sink which in our case in CMainFrame itself.We used GetIDispatch() to get the IDispatch pointer associated with this class.See MSDN help for more info on GetIDispatch().We get back a cookie which is used to break the connection later. The rest of the code is simple.One important thing is MainFrame.h where we imported the ServerWCP.tlb.There is a Dispatch map also which is used to describe the handlers Look at the following entry in the Dispatch map.


DISP_FUNCTION_ID(CMainFrame, "AdditionDone",1, OnAddDone, VT_EMPTY, VTS_I2)

This basically maps the DispID with the function pointer.Here Disp ID 1 is mapped to OnAddDone.The last two paramters describe the return value and the arguments to the function.So whenever the system wants to invoke the method with disp ID 1 OnAddDone is called.

Rest is simple MFC coding.

That was about implementing the sink in MFC.Things are a little bit different when you implement sink in ATL.Next we will implement the sink in ATL which will be shown step by step.You can however download the ATLSInkApp and register it.You will also download a console application which does nothing but invokes a method on object in ATLSinkApp

ATL Sink

We are going to now implement a sink in ATL .The sink won't do any fancy things except for displaying a message box when it gets the event from the ServerWCP.Remember our source interface was a dispinterface.


[default, source] dispinterface _ICoCalculatorEvents;
It means we need to implement a IDispatch interface as a sink.One method is to simply derive an interface from IDispatch and implement all the methods or at least Invoke(for others you may return E_NOIMPL for simplicity).But the Invoke method is not easy to implement for maintainence reasons.But in our sample we can implement a IDispatch interface.Lets do that also but then our whole point of this section ATL Sink will be defeated.We can do that in a raw C++ code also.So lets take a peek into what ATL has got for us.ATL provides two template classes IDispEventImpl and IDispEventSimpleImpl for providing implementation of IDispatch.Note there is one more template class called IDispatchImpl But IDispatchImpl provides a default implementation of IDispatch portion of any dual interface. IDispatchImpl needs a dual interface and not a dispinterface as its template paramter.But our source is a dispinterface and not dual So we cannot use it

Now which one of IDispEventImpl and IDispEventSimpleImpl to use.The former uses type library to determine at runtime which method to call..The later doesnot use any type library and is efficient.IDispEventSimpleImpl takes three parameters,the first one is an nID event source identifier which should be unique,the deriving class T and the GUID of the dispinterface.

Now lets begin I will explain more things as we go.Create a new project by selecting File New Projects and then ATL COM AppWizard.Type ATLSinkApp as the project name and add it to the current project.(Note you can simply download the ATLSinkApp provided here and compile it if you donot want to do a step by step work)Choose Server type as Dll and press finish.

Now insert a new ATL object into the ATLSinkApp project.Call this object CoCaller and in the attribute tab select Free as threading model.Select Custom for interface type and say No to Aggregation and donot check anything else.Now add a new method Method01() to the ICoCaller interface.This method doesnot take any paramters.Next copy two files downloaded called EventSink.cpp and EventSink.h into the project.Add these files into the project.EventSink.h declares a class CEventSInk which is derived from IDispEventSimpleImpl You can see there are somethings in the file which I didnot explain before.If you go to the top of the file you will see there are two const int's one is disp ID of the method in our _ICoCalculatorEvents interface and another is the template paramter to be passed to the IDispEventSimpleImpl.which can be any arbitary value.Next you can see down at the bottom there is what is called a sink map.When the source of events calls fires the events it calls the Invoke of IDispEventSimpleImpl with the Disp ID.IDispEventSimpleImpl searches this sink map for the Disp ID and determines which function to call.As I told earlier that IDispEventImpl get the type info from the type library at runtime ,but IDispEventSimpleImpl doesnot need type lib.It gets the info at the compile time.How does it get that type information about the methods at the compile time.We provide that info through a struct called _ATL_FUNC_INFO.It has several members the first one is the calling convention,second is the return type of the method(which is void in case of our AdditionDone)so we specify VT_EMPTY ,third is number of parameters which we specify as 1 and the last one is the array of VARTYPE where we define the type of each parameter.In our case we have only one paramter of type short so we deifine VT_I2 as the type.We need to pass this information in the SInk entry for each method.So that is why we passes this structure as the paramter(last one) to SINK_ENTRY_INFO macro


SINK_ENTRY_INFO \
(EVENT_ID,DIID__ICoCalculatorEvents,DISPID_ADDITION_DONE, \
  OnAdditionDone,& OnAdditionDoneInfo)
In the stdafx.h type the following line


#import "..\ServerWCP\ServerWCP.tlb" no_namespace named_guids
This will give us the smart pointer ICoCalculatorPtr. Now define a ICoCalculatorPtr pointer called spServer in the cocaller.h.You can see we need let the server know of our implementation of our sink.We do that by calling DispEventAdvise method of IDispEventSimpleImpl(see atlcom.h for details).To break connection we need to call DispEventUnadvise().Cookie business is maintained within the class.Actually DispEventAdvise and DispEventUnadvise call AtlAdvise and AtlUnadvise methods which internally call the raw adivise and unadvise on IConnectionPoint interface.Thats it.Rest of the code is simple you can copy paste the code from the dowloads.We call DispUnadvise in FinalRelease().

Next insert into project main.dsp from the Console directory .You may wish to debug the code.Run the code from ATLSInkApp and keep the breakpoints at OnAdditionDone and finalrelease. And specify main.exe as the exe from which to load the dll.

You will see the MessageBox and finally code will go to FinalRelease where DispUnadvise is called to break the connection.

Thats all in this tutorial.I hope you must have got a good understanding an a practice session for implementing the connection points in both ATL and MFC.

Downloads

Download source code - 45 Kb


Comments

  • Excellent Article

    Posted by spnene on 05/05/2005 10:00pm

    There is very good artcile on COM / Connection Points in MFC.

    Reply
  • Very good article !!

    Posted by Legacy on 02/20/2004 12:00am

    Originally posted by: Pavi

    You have given savvy examples for connection points. After reading your articles, i could go ahead and write my own code for them. Thanks many times !

    Reply
  • Connection Points and Asynchronous Calls - Part II

    Posted by Legacy on 11/11/2003 12:00am

    Originally posted by: George

    I tried these methods to catch events from the objects of Microsoft Access but I didn't succed. Have somebody tried this. Please help!

    Reply
  • Connection point

    Posted by Legacy on 09/11/2003 12:00am

    Originally posted by: shahid

    how to implement C.point using CCmTarget and CConnectionPoint only in MFC.
    

    Reply
  • connection point & IUnknown

    Posted by Legacy on 01/16/2003 12:00am

    Originally posted by: hannah parker

    Back in 2000, the following question was asked...

    "The Part II example is using IDispatch for the sink interfaces. Have you also tried it using IUnknown for the sink interfaces, or is it that ATL only supports IDispatch for ConnectionPoints???
    Are there any performance differences in using ConnectionPoint compared to the custom callback interface?"

    I am looking for an answer to the same question... Any advice?

    Thanks

    Hannah

    Reply
  • Clients going down

    Posted by Legacy on 03/15/2002 12:00am

    Originally posted by: AndyC

    What would be the best way for the server to handle client connections when the client is closed (or crashed) with out unadvising the server.

    Cheers
    Andy

    Reply
  • Hi Dhar!

    Posted by Legacy on 02/13/2002 12:00am

    Originally posted by: Nihit Saxena

    Hi Dhar How are you,.

    Nihit

    Reply
  • Thankyou!!!

    Posted by Legacy on 08/15/2001 12:00am

    Originally posted by: Scott Guillaudeu

    Thankyou _SO_ much for your article and sample project. I've been frustrated by examples in a book (Beginning ATL COM) not working with VC6, and the Dr. GUI article series cut short one article (the most important one--the client!!). Using your sample code, I got my program working. Thankyou, thankyou, thankyou!!!

    Reply
  • MFC Exe. Server sending Events(not using ATL)

    Posted by Legacy on 11/23/2000 12:00am

    Originally posted by: Saleem U

    How do you implement a MFC Executable (remote) Server without using ATL that sends events to a MFC Executable Client?
    

    Reply
  • Handling events with COM object as param

    Posted by Legacy on 10/14/2000 12:00am

    Originally posted by: Jean-Pascal Chauvet

    Hi,

    This article is very useful and completes quite well MSDN samples on the subject. On topic that is not covered anywhere (and I spent days looking) is when the event has a COM object as a parameter. Say for instance in your sample that the addition is not on 2 integers but on 2 matrixes. The OnAdditionDone event would therefore pass a IMatrix * obj (VT_DISPATCH) pointer instead of an integer (VT_I2).

    How would you implement the event handler on the client side, and how would you actually start using the IMatrix pointer to access its members (tried everything I could think of, wither it doesn't compile or compiles but crashes). In other words, how to complete this:

    void CCoCaller::OnAdditionDone(IMatrix *result)
    {
    // How to implement this so that it works:
    BSTR str; result->get_Name(str);
    }

    MSDN gives 2 samples on how to implement IDispEventSimpleImpl and IDispEventImpl using Word and Excel veents, none of them show events that pass COM objects as parameters.

    Any help would be very useful. Thanks a lot

    JP

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • IBM Worklight is a mobile application development platform that lets you extend your business to mobile devices. It is designed to provide an open, comprehensive platform to build, run and manage HTML5, hybrid and native mobile apps.

  • New IT trends to support worker mobility — such as VDI and BYOD — are quickly gaining interest and adoption. But just as with any new trend, there are concerns and pitfalls to avoid.  Download this paper to learn the most important considerations to keep in mind for your VDI project.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds