Connection Points And Asynchronous calls

Connection Points are a way of letting Server to notify you when something has happened in the server which you wanted to know of.I started using connection points when I was just learning how to code in COM.I came across a lot of issues including the performance .Eventually I learned the how and whys (not 100% :)) of connection points.This article is a kind of a tutorial for those who donot know what connection points are or those who know what they are but haven't ever tried to code them.This article tells you why do you need connection points and what are the alternatives.The article takes you slowly from the need of getting the events back from client to why you need Connection Points at all.Connection Points are implemented using IDispatch interface .This tutorial simulates connection point using Custom Interfaces(IUnknown).

Given the length of the article I have divided this into two parts .Part I,which is this, discusses Aynchronous calls and why are they useful.This part also tells you about the callbacks .Here we will implement a Server (called Server03) which will demonstrate the need of asynchronous calls .This server will simulate connection points with the IUnknown callback interfaceWe will also implement one MFC client for the server..

Part II of this article will demonstrate the connection points in ATL and we will implement a client in ATL.and will see what are the different things one need to take into consideration while implementing a sink in ATL.

Why do we need notifications from server?

All COM calls are synchronous meaning when you make a call to a method on an interface ,the thread blocks until the method returns.Well being synchronous is not bad always but sometimes it is painful.Lets say we have an CoMath object exposes the following method


Add([in]short nFirst t,[in] short nSecond,[out,retval] short* pResult);
...and the implementation is something like this:

STDMETHODIMP CoMath::SomeArithmatics([in]short nFirst,[out,retval] short*pResult)
{
 *pResult = 0;
 try
 {
  while(nFirst-- != 10);
   //do something here...
 }
 catch(...)
 {
  //catch exceptions here...
 }
 return S_OK;
}
Now lets say some poor user calls the interface method like this short lParam = 9;

short lResult;

IMathItfPtr->Add(lParam,&lResult);
This call makes the method to execute for ever (nFirst never reaches 10) making the caller to hang too.

Now that we know it is a problem What is the solution?There can possible be many solutions to this problem one of which of course is not to write that bad code first of all.The code shown was not at all a commercial code and but trust me you will definitely find such situations even with the well written code.

The better solution is aysnchronous calls.Well speaking of today there is only one COM call which is asynchronous.In future (COM+)you can see more Asynchronous COM methods.

The aysnchronous calls mean that you send out a command to the Server and you donot need to wait for the call to finish .The server does its job and when it finishes the task it calls the client back.

Note that I put the calls word in bold on purpose.This is where our basic of connection points starts...Server calls you back. How can that be possible ? How does server know what method to call?I will explain that in detail in a little while.But first lets look how will the aysnchronous thing look like.

Server implements BeginOperation()and stores the paramters in a Structure and return immediately.Now client no longer waits for the operation to complete and does some other work.Meanwhile server completes the task and says "ok I am done. who wants to know!" and informs all the interested clients.

So we need to tell server of some our method that server can call back when it is done what it was doing.If server has more than one client every client has to tell server the name of the method which server should call in case of an event.And that becomes the problem.Server will now need to be kind of hardcoded for each and every cleint method.Wouldn't it be easy if Server always calls the same method whenever it finshes athe job.In other words instead of client specifying the method name ,server decides what funtion will it call when it finishes the task.And every client implements the same method.Now lets say if server has a number of events to raise then we will need a number of methods.and server is NOT going to implement these methods .In other words Server defines an interface which define the various methods which server will call when it needs to send some notification(or event) to its clients.All the clients need to advise the server of its implementation (called sink) of the interface.Server can maintain a list of all the interested clients and whenever there is an event which server wants to send to the interesting clients ,it will simply travers through the list and send events to all the clients.

What ever I talked about till now is all what connection points is about.The interface which is implemented by the client is basically the sink.The server which publishes the interface is called source.When the client implements the interface(sink) published by the source it needs to let the server know of this interface .In COM client advises the Server of the sink interface

Now how does it work.So lets start .

First of all we will write a server(object) which will expose a method called Add which takes a short paramter and it raises event whenever it finishes the task.We call our server Server03.

Server03 publishes an interface called ICallBackItf which contains a method called OnTaskFinished() which will be called by the server whenever it wants to send an event to the interested clients. All clients which want to receive notifications need to implement this interface .That implementation of the interface is called as sink .Client needs to tell server about the sink.For that purpose ] the server has a method called AdviseCallBk() method which takes an IUnknown pointer as an [in parameter and returns a connection so that whenever client Cookie.Cookie is needed to uniquely identify what we call wants it can break the connection.For breaking the connection there is a corresponding UnAdviseCallBk() which takes the Cookie as a paramter.

Things will become more clear when we will see the real code.

To create the server follow the steps below

Start the Visual C++ IDE and Select File New and TYPE

We want to create a DLL server so just press Finish on the next Page

So lets insert our object .Selected Insert and New ATL Object .You will see the object wizard .

Double click on simple object and enter the Short Name as CoMathServer03.

Goto to the attributes tab and select the Custom interface and say No to Aggregation

Press OK and you will get an IDL file called Server03.idl

Now goto Class View tab in Workspace pane and right click on ICOMathServer03 interface and Select Add Method from the menu.Enter the method Add

and in Parameters type [in] short nNumber as shown .Similarly add AdviseCallBk and UnAdviseCallBk.

We are done with all wizards and everything.Next we will add follow IDL file called Callback.idl and we will add the following defintion in it


import "oaidl.idl";
import "ocidl.idl";
[
	object,
	uuid(20846DF1-B4CA-11d3-971F-0050047D51FB), 
	helpstring("ICallBackItf Interface"),
	pointer_default(unique)
]
interface ICallBackItf : IUnknown
{
	[helpstring("method OnTaskFinished")] HRESULT OnTaskFinished([in] short lResult);
};
This is the interface our server wants all the clients who want notifications back to be implemented.

We put it in a seperate IDL because of the reason that we will need to implement the same interface in the client also So we will just include this IDL in client side and we should be Ok.

Now we modify the original Server03.IDL file to import the CallBack.IDL by adding this statement


import "CallBack.idl";
Ok now lets go bak to the implementation of the CoMathServer03.Here is the implementation of the Add method

STDMETHODIMP CCoMathServer03::Add(short nNumber)
{
	::EnterCriticalSection(&mLock) ;
	m_DataFromClient = nNumber;
	:LeaveCriticalSection(&mLock) ;
	unsigned long hThreadHandle = 
			_beginthread(ProcessingThreadFn,//routine that starts thread...
				     0, //stack size 0 = use same as of primary thread...
				    (void*)(this)//argument...
			 	   );
	return S_OK;
}
Don't worry the code is simple and you will understand the meaning and need of each and every statement soon. First of all whenever a clien calls Add method we store the parameter in a data member called m_DataFromClient.Since we are talking about a simple server we used a data memeber which is also a short type.Advanced serves may think of STL Queues etc.Now whenever we store the parameter passed,we will protect our data member so that we donot overwrite the data.This reminds me of one thing .STA also sometimes need synchronization.Anyway,we create a thread with _beginthread() API.which takes the address of the starting routine of the thread (ProcessingThreadFn in our case) the second param is stack size which we passed 0 (indicating use the same value as that of primary thread) and lastly we passed an paramter to the routine(we passed this for simplicity).So whenever a client calls an add method the method stores the paramter passed and creates a thread and returns immediately.So client never waits .

Interesting method is AdviseCallBk


STDMETHODIMP CCoMathServer03::AdviseCallBk(IUnknown *pICallBk, long *Cookie)
{
	ATLASSERT(pICallBk != 0 );
	*Cookie = m_vecCallBk.Add(pICallBk);
	return S_OK;
}
Firstly we assert that IUnknown pointer is vaid.Look at the next line m_vecCallBk is an object of CComDynamicUnkArray class.Imagine a case where several clients will be interested in the events and they all advise the server of their respective implementations of call back interfaces.So we need to store all the interface pointers and whenever we want to send a notification we traverse through the list and call methods on each and every interface implementation That way all the interested clients are notified.CComDynamicUnkArray is used to hold the dynamically allocated IUnknown pointers.When we call Add method of the class with IUnknown Pointer what we get back is an index in the array so that we recognize our IUnknown pointer.So,we return the index to the client as Cookie which client can store and then pass us whenever it tries to unadvise the Server of its sink.At Server level all we need to do is find the IUnknown pointer at that particular index and then remove that from the list So next time whenever Server has something to nitify clients with ,it does not fire the event to the unadvised client as the IUnknow pointer no longer exists in the list.

The remaining of the code is very simple.One method which needs a little explaination is Fire_AdditionDone()


HRESULT CCoMathServer03::Fire_AdditionDone(short lResult)
{
	//for each client send the event...
	for(IUnknown** pp =m_vecCallBk.begin();pp < m_vecCallBk.end();++pp)
	{
		if(*pp)
		{
			CComQIPtr pCallbackPtr(*pp);
			ATLASSERT(pCallbackPtr!=NULL);
			pCallbackPtr->OnTaskFinished(lResult);
		}
	}
	return S_OK;
}
The Server loops through the IUnknown list and QIs the IUnknown for ICallBackItf..If the IUnknown pointer for the object supplied does not implement the ICallBackItf we assert.If it is there we simply call the OnTaskFinished method.One more thing important here is that our secondary thread is having a Sleep() statement in it to simulate a long operation.

If you have understood till this point ,the client should not be any mystery .As you can expect that a typical client will create an instance of our componenent CoMathServer03 and if interested in receiving events(which in our current example will be) will call the AdviseCallBk() passing the IUnknown pointer of the sink.At some point client UnAdviseCallBk() passing the Cookie.and it will no longer receive any events.

The MFC client code is very simple .I have created an MFC client called Server03Client .There is a menu item called CallBack which pops up a dialog called Connection Dialog.

If you look into the source files of the client you should find the file called IMathSvr03Imp.h This file is the sink we have implemented in the MFC.The ConnectionDlg.cpp/.h files contain the class for connection dialog .And in IDL we included


 import "..\CallBack.idl";
This inserts the definiton of ICallBackItf and if you see IMathSvr03Imp.h you will find that the CIMathSvr03Impl is implenting the ICallBackItf.

Note: I have added ATL support to this MFC Client just to make things easier .My aim was to show you how we how the callback works and not to teach you MFC etc.If you wish you can implent the client in purely MFC which is a exercise to you.

Register the Server and run the client .Select CallBack from the main menu and you get this dialog

Enter some value say 10 in Number edit box and press Fetch.Immediately type in 20 in the Number and press Fetch again.

You will get two MessageBoxes one after another displaying 100 and 200 respectively.Actually server multiplies Number by 10 .and return You need not to wait till it finshes the operation .You can enter another value and when server finishes task it indicates the result.

Now you can see that your main thread need not to wait for the server to return before you can start any further work.

Advantages

There are several advantages of the aysnchronous calls .Lets say your server is actually talking over the network to some other server and waiting for some data to come back.Lets say it is waiting for an event to be set WaitForSingleObject() and using INFINTE as the timeout parameter.Lets say it never gets back the data and therefore no the event is never set and your poor client is waiiting for ever.It cannot even timeout.

In the second part of this article we will develop the Server with Connection point.and will see how it works.

Note: When compiling the source code for client,goto Project Settings>>C++ and from Category choose Preprocesor and in the 'Additional include Directories"' enter the path where you extract the server03.

Download Notes

Source code Server.zip contains the server.Extract the files to a directory .say c:\Code
Client.zip conatins the client code Extract it to c:\ Code (for simplicity)
Compile server03 first and then Client.

Downloads

Download client demo source code - 14 Kb
Download server demo source code - 26 Kb


Comments

  • Some helpful hints

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

    Originally posted by: Murthy Ramchanvasa

    I was wondering why server not have cocreate instance in second thread. Because you know, like em these things, you cannot have thread in threads without code in middle somewhere. Doing so will not work, should not work. I code for many moonfaces white men and they say curry can't code, but I say unto them curry can code yes.

    Reply
  • Callback fails when calling from vb client

    Posted by Legacy on 11/14/2002 12:00am

    Originally posted by: Debarghya Bhattacharjee

    This topic is very much usefull but when I am tring callback from vb client it give error like "can't read from memory location ...".I am doing a project with com with callback support.If any knows the solution please help me.

    Reply
  • Connection Points and Asynchronous Calls - Part II

    Posted by Legacy on 09/28/2002 12:00am

    Originally posted by: Mathew George

    Hello friends,
    After reading the article I tried to make a connectable object.The object was succesful since I tried it with a VB Client.But my VC++ client didnt work(I think the dispatch parameter is not getting assigned any value)
    Please try to solve this problem.
    regards,
    Mathew

    Reply
  • DCOM

    Posted by Legacy on 08/23/2002 12:00am

    Originally posted by: John Lagos

    It works perfectly.
    Is it possible to use the same code as a DCOM.
    In other words to put the Server on another machine!

    Thank you in advance

    Reply
  • My custom interface is not working

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

    Originally posted by: Gabi

    In your presentation, you said that if we include CallBack.idl in the client side, we should be OK. Unfortunately, we aren't OK, because I simply did not manage to make my server (MFC server with ATL support) send any notifications back to my client ( which is based on MFC with ATL support). You probably ignored to present some very important steps in defining the ICallBack interface, because just following your presentation, we get nowhere.

    Reply
  • notification to a VB client

    Posted by Legacy on 01/10/2002 12:00am

    Originally posted by: vishal

    All this disussion and other aticles i read abt LCE events in com+ seem to be about notification from one DLL to another DLL.

    Can somebody please explain how a VB client application may receive event notification from a remote DLL configured under com+ ?

    thanks

    Reply
  • Data sending from inproc server to another server

    Posted by Legacy on 11/03/2001 12:00am

    Originally posted by: Mahbubur Rahman

    Hi
    How I can transfer the result from inproc server to another
    process in the same machine?

    Reply
  • Pure MFC

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

    Originally posted by: Issac

    Dose somebody has an idea how to impliment the client just using MFC?

    Reply
  • server to server notification

    Posted by Legacy on 09/20/2000 12:00am

    Originally posted by: rama

    how do you notify a server acts as a sink and made the request from one of its thread.

    thanks
    rama..

    Reply
  • Why is there no marshaling code when you fire events in second thread?

    Posted by Legacy on 04/18/2000 12:00am

    Originally posted by: Weidong He

    Why is there no marshaling code when you fire events in second thread? Could you tell me how to access the sink interface at another thread if there no marshaling code.

    Reply
  • Loading, Please Wait ...

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

Top White Papers and Webcasts

  • The impact of a data loss event can be significant. Real-time data is essential to remaining competitive. Many companies can no longer afford to rely on a truck arriving each day to take backup tapes offsite. For most companies, a cloud backup and recovery solution will eliminate, or significantly reduce, IT resources related to the mundane task of backup and allow your resources to be redeployed to more strategic projects. The cloud - can now be comfortable for you – with 100% recovery from anywhere all …

  • With JRebel, developers get to see their code changes immediately, fine-tune their code with incremental changes, debug, explore and deploy their code with ease (both locally and remotely), and ultimately spend more time coding instead of waiting for the dreaded application redeploy to finish. Every time a developer tests a code change it takes minutes to build and deploy the application. JRebel keeps the app server running at all times, so testing is instantaneous and interactive.

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds