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

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read