Click here to Skip to main content
Email Password   helpLost your password?

Introduction

This article is intended to explain the concept behind connection points with a clear practical example, which will demonstrate an in-process COM server and an MFC client that uses the server.

What Exactly Is It?

It is a method used by a COM object to call back to the client. In other words, clients get call back notification from the COM object.

Perhaps you have heard about callback functions. Well, it goes like this. Suppose you have a COM object that exposes an interface IArithematic, and has a resource intensive method, say Add(int a,int b) - ("Anything for simplicity," as the hermit said when he took up residence naked in an Himalayan cave. Sam Weller�Pickwick Papers). Imagine that this method is going to take a lot of time and you do not want to wait until that task is finished. You can use that time for something. So here is where a connection point comes in. You assign a function ExecutionOver(int Result) in your client code, which the COM object can call after it has finished executing the Add method.

So, when the COM object is finished with the task, it calls the client function ExecutionOver (passing the result of addition). The client happily pops up the result in a message box. That's the whole gist. We shall go into the details now.

How Does the COM Object Know How to Call ExecutionOver??

Imagine that the client exposes an interface ISink, which has a method ExecutionOver(int result). Now, if the client can pass this interface to the COM object, the COM object can happily call ExecutionOver. For example, in the COM the code fragment may look like this:

//===================================================

ISink *pClientSink;
//(Client somehow passes the ISink interface pointer

//we shall see how later -- so pClientSink is loaded now

HRESULT Add(int a , int b)
{
  pClientSink->ExecutionOver(a+b);
}
//=====================================================

This is what really happens. The rest is for making this whole thing generic enough. Microsoft has implemented this by defining connectable objects. Let's start by examining the COM interfaces involved in connections - IConnectionPoint and IConnectionPointContainer. The object (rather than the client) implements both these interfaces.

Both the interfaces are shown below.

interface IConnectionPointContainer : IUnknown {
  HRESULT EnumConnectionPoints(
    IEnumConnectionPoints **ppEnum) = 0;
  HRESULT FindConnectionPoint(REFIID riid,
    IConnectionPoint **ppCP) = 0;
};

interface IConnectionPoint : IUnknown {
  HRESULT GetConnectionInterface(IID *pIID) = 0;
  HRESULT GetConnectionPointContainer(
    IConnectionPointContainer **ppCPC) = 0;
  HRESULT Advise(IUnknown *pUnk, DWORD *pdwCookie) = 0;
  HRESULT Unadvise(DWORD dwCookie) = 0;
  HRESULT EnumConnections(IEnumConnections **ppEnum) = 0;
};

Now, let's go one step at a time and see how the whole thing works.

A COM client calls CoCreateInstance to create the COM object. Once the client has an initial interface, the client can ask the object whether it supports any outgoing interfaces by calling QueryInterface for IConnectionPointContainer. If the object answers "yes" by handing back a valid pointer, the client knows it can attempt to establish a connection.

Once the client knows the object supports outgoing interfaces (in other words, is capable of calling back to the client), the client can ask for a specific outgoing interface by calling IConnectionPointContainer::FindConnectionPoint, using the GUID that represents the desired interface. If the object implements that outgoing interface, the object hands back a pointer to that connection point. At that point, the client uses that IConnectionPoint interface pointer and calls IConnectionPoint::Advise( [in] IUnknown *pUnk, [out] DWORD *pdwCookie) to hand over its implementation of the callback interface so that the object can call back to the client. To make it clear once more, the pointer to IUnknown passed to the advise method is the pointer of an interface that's defined and implemented in the client EXE.

Okay, now let's illustrate the whole thing by a practical example.

  1. Create a new ATL-COM AppWizard Project and name it ConnectionCOM.
  2. Right-click on the class view and create a new ATL object.

    Name it Add (interface IAdd).

    Before you click the OK button, be sure to check the Support Connection point checkbox.

    Click OK.

Note the classes generated in the class view. You will find one IAdd and _IAddEvents. The latter is just a proxy class; it is to be implemented on the client side. It came because we ticked the Connection_Points check box.

Add a method 'Add(int a,int b) ' to IAdd interface and a method 'ExecutionOver(int Result)' to the _IAddEventsInterface. The class view will be as shown below.

But because we have selected a dual interface and we do not need all that hassle, let's take out the support for IDispatch by editing the IDL file. Below is the original file.

//===========================================================

// ConnectionCOM.idl : IDL source for ConnectionCOM.dll

//

  :
  :

library CONNECTIONCOMLib
{
importlib("stdole32.tlb");
importlib("stdole2.tlb");

[
  uuid(AFE854B0-246F-4B66-B26F-A1060225C71C),
  helpstring("_IAddEvents Interface")
]
// Old block - take this out

// dispinterface _IAddEvents

// {

// properties:

// methods:

// [id(1), helpstring("method ExecutionOver")]

// HRESULT ExecutionOver(intResult);

// };

//To this one -put this in

interface _IAddEvents : IUnknown
  {
  [id(1), helpstring("method ExecutionOver")] HRESULT
          ExecutionOver(intResult);
  };
  [
    uuid(630B3CD3-DDB1-43CE-AD2F-4F57DC54D5D0),
    helpstring("Add Class")
  ]
  coclass Add
  {
  [default] interface IAdd;
  //[default, source] dispinterface _IAddEvents; take this line

  //out and put the line below in

  [default, source] interface _IAddEvents ;
  };
};

//================================================================

Whew! The client side is almost finished now. Now, do a build because we need the type library to do a neat thing with ATL. Now, right-click on the CoClass and click Implement Connection Point.

Check _IAddEvents in the ensuing dialog box.

A CProxy_IAddEvets class is generated with the Fire_ExecutionOver(int result) method. This will take care of how the COM object will call the client interface (and takes care of multiple clients calling the same COM DLL and other such issues). Now, let's implement our old IAdd Interface Add method.

//=====================================================


STDMETHODIMP CAdd::Add(int a, int b)
{
// TODO: Add your implementation code here


Sleep(2000);   // to simulate a long process


//OK, process over now; let's notify the client


Fire_ExecutionOver(a+b);

return S_OK;
}
//======================================================

Do a build and the COM is ready. Make sure that the COM is registered.

Now the Client Side

Create a new MFC AppWIzard(exe) Dialog based project - ConnectionClient. It looks like this:

Now comes the main part.

We create a CSink class that derives form _IAddEvents. You can use the class wizard for it. You have to supply the header where the _IAddEvents interface is defined. For that, copy the ConnectionCOM.h and ConnectionCOM.tlb files to your client EXE's project folder and add these lines to Sink.h file.

#include "ConnectionCOM.h"

#import "ConnectionCOM.tlb" named_guids raw_interfaces_only

Now we have the additional task of implementing each method defined in the _IAddEvents interface. (Never forget that a COM interface is just a pure abstract base class and that the derived class has to implement all of its methods.)

So, let's implement the first ExecutionOver:

STDMETHODIMP ExecutionOver(int Result)
  {
  CString strTemp;
  strTemp.Format("The result is %d", Result);
  AfxMessageBox(strTemp);
  return S_OK;;

  };

Now comes QueryInterface, AddRef, and Release.

HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void
                                         **ppvObject)
  {
    if (iid == IID__IAddEvents)
    {
      m_dwRefCount++;
      *ppvObject = (void *)this;
      return S_OK;
    }
    if (iid == IID_IUnknown)
    {
      m_dwRefCount++;
      *ppvObject = (void *)this;
      return S_OK;
    }
    return E_NOINTERFACE;
  }

ULONG STDMETHODCALLTYPE AddRef()
  {
    m_dwRefCount++;
    return m_dwRefCount;
  }

ULONG STDMETHODCALLTYPE Release()
  {
    ULONG l;
    l  = m_dwRefCount--;
    if ( 0 == m_dwRefCount)
       delete this;

    return l;
  }

We are now almost there.

Now, on the dialog class on the SendToServer Button Click event, we shall do the last bit of coding.

#include "Sink.h"          // for our CSink class

#include <atlbase.h>       // for ATL smart pointers


void CConnectionClientDlg::OnSendToServer()
     //SendToServer button click event

{
UpdateData(1);
HRESULT hr;

//call CoInitialize for COM initialisation


hr =CoInitialize(NULL);
if(hr != S_OK)
  return -1;

// create an instance of the COM object


CComPtr<IAdd> pAdd;
hr =pAdd.CoCreateInstance(CLSID_Add);
if(hr != S_OK)
  return -1;

IConnectionPointContainer  * pCPC;
  //IConnectionPoint       * pCP;

  //these are declared as a dialog's member

  //DWORD                  dwAdvise;

  //variables,shown here for completeness


  //check if this interface supports connectable objects


  hr = pAdd->QueryInterface(IID_IConnectionPointContainer,
                           (void **)&pCPC);

  if ( !SUCCEEDED(hr) )
  {
    return hr;
  }

  //

  //OK, it does; now get the correct connection point interface

  //in our case IID_IAddEvents


  hr = pCPC->FindConnectionPoint(IID__IAddEvents,&pCP);

if ( !SUCCEEDED(hr) )
  {
    return hr;
  }

//we are done with the connection point container interface


pCPC->Release();

IUnknown *pSinkUnk;

// create a notification object from our CSink class

//


CSink *pSink;
  pSink = new CSink;

  if ( NULL == pSink )
  {
    return E_FAIL;
  }

  //Get the pointer to CSink's IUnknown pointer (note we have

  //implemented all this QueryInterface stuff earlier in our

  //CSinkclass


hr = pSink->QueryInterface (IID_IUnknown,(void **)&pSinkUnk);

//Pass it to the COM through the COM's _IAddEvents

//interface (pCP) Advise method; Note that (pCP) was retrieved

//through the earlier FindConnectoinPoint call

//This is how the com gets our interface, so that it just needs

//to call the interface method when it has to notify us


hr = pCP->Advise(pSinkUnk,&dwAdvise);

//dwAdvise is the number returned, through which

//IConnectionPoint:UnAdvise is called to break the connection


//now call the COM's add method, passing in 2 numbers

  pAdd->Add(m_number1 ,m_number2);
//do whatever u want here; once addition is here a message box

//will pop up showing the result


//pCP->Unadvise(dwAdvise); call this when you need to

//disconnect from server

pCP->Release();

  return hr;
}

Now, do a build of the dialog EXE. Now fire away. That's it for connection points.

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
QuestionStucked at the point of adding CSink class derived from _IAddEvents.
Member 3714305
18:39 25 Sep '08  
Hi I used CTRL + W to open the class wizard then tried adding a class "From a Type library". Before to that I have copied the ConnectionCOM.h and ConnectionCOM.tlb files to mu client project directory. But When I tried adding a new class I only found IAdd interface But the other class was not there. Could you please help me procede forward
GeneralCompilation on VC .Net 2003
Mitendra Anand
0:15 9 Mar '07  
Here is the experience when I tried to compile on VC .Net 2003 the uploaded source code for this article:-

1) Compiling ConnectionCOM
Performing registration
'regsvr32' is not recognized as an internal or external command.

Unfortunately I was unable to solve this I had to manually register it at Run promptFrown

2) Compiling ConnectionClient
afx_msg int OnSendToServer();

Following line gave compilation for incorrect type casting. Since OnSendToServer() is returning 'int'
ON_BN_CLICKED(IDC_BUTTON1, OnSendToServer)

I have a understanding MessageMaps have changed from VC 7 onwards and they dont expect MessageHandlers(OnSendToServer) with parameters and return type.

so I did this change:-
afx_msg void OnSendToServer();
And corresponding changes in implementation of this method.

And then I was able execute it correctly Smile


Purpose of this post is to help somebody if anybody doing the same AND most importantly comment on my assumptions!






Mitendra Anand
Development Engineer
ABB India

mitendra.anand@in.abb.com
QuestionHOW the ConnectionCOM dll calls ExecutionOver from VB?
thelight
19:54 19 Dec '06  

How VB takes care of those ConnectionCOM.h and .tlb files where _IAddEvents been defined....!

Do we need to Addref, QueryINterface and all those stuff in VB also?

Could someone please help me?



Thanks in advance
Generalsimple and easy
lallous
22:52 28 Aug '06  
Hello

Thank you for this simple and easy article for introducing the basics of connection points.

You got my 5
QuestionConnectionPoint in EXE
nareshkavali
5:09 9 Aug '06  
Smile hi!
can any one help me in creating an exe based component and client using connectionpointer in ATL
thier will be .dll,.exe and report
i had made .dll type connectionpointers
iam not getting how to do .exe type



naresh
GeneralConnection Point
mehdi_ab
23:32 28 Apr '06  
Dear sir,

I am working in VS 2003.NET and when i want to instanciate the Sink class, the compiler returns an error that says
-your class is abstract and could not to be instanciate.
when i cheked my dispatch interface that the Sink class inherited from it, i saw that there are 4 onother methods like Invoke, .... in my disoatch interface.
Now, what i have to do ? i have to implement those methodes or not ?



Bests,
Mehdi Abolghasemi
GeneralRe: Connection Point
xue231
3:16 10 May '06  
the ISink interface in the article is derived from Iknown. and yours is derived from dispinterface, so some errors happen!
GeneralRe: Connection Point
tmaheshwari
22:01 14 May '06  
i am also facing the same problem.. please help me out.. how to reslove it. i could not found any class with name 'ISink' ... there is only one CSink class.

class CSink : public _IAddEvents

Now... while implementing CSink, i have to implement 4 more function (excluding the 3 previous ones) ....

please suggest... what can i do ....???

tapesh
GeneralRe: Connection Point
tmaheshwari
22:59 14 May '06  
Ohh!! I got it...

I forget to chagne the idl file: interface _IAddEvents : IUnKnown
now i am able to compile client also.

The next problem is, while calling Add() function, server does not call ExecutionOver() implemented on client side.

In the template class CProxy_IAddEvents, in the function Fire_ExecutionOver() this line returns a negative number and not executing callback.

ret = p_IMyObjectEvents->ExecutionOver(result);

Please help, what i need to do to resolve this.

tapesh
GeneralConnection Point VC7
M Atif
9:57 9 Mar '06  
Hi,

I m trying to implement connection point using VC7 and got some questions...

u know when we create and build the dll its create proxy class ....in ConnectionCOM it is creating
in CONNECTIONCOMCP.h.....

I m not getting any idea ...where it does create when we do it in VC7 (.net )

Looking forward for your reply.


Regards,
GeneralBeautiful example
BilloKhan
16:59 3 Jan '06  
Beautiful example but I wish there was working example of application. Eventually I was able to finish the example. I was able to compile with VS 2005. It is not my article, so I am unable to upload the whole project, but if someone needs it I can email the whole project.
Roll eyes

Agha Khan
Generalhelpp
Dileep_kerala
20:52 25 Dec '05  
hi,
i tried to create a sample pgm following ur tutorial. I got stuck with an error now. please help.



E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : error C2259: 'CSink' : cannot instantiate abstract class due to following members:
e:\vc study examples\com_atl\fire\client\sink.h(15) : see declaration of 'CSink'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::GetTypeInfoCount(unsigned int *)' : pure virtual function was not defined
e:\vc98\include\oaidl.h(2697) : see declaration of 'GetTypeInfoCount'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )' : pure virtual function was not defined
e:\vc98\include\oaidl.h(2700) : see declaration of 'GetTypeInfo'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *)' : pure virtual function was not defined
e:\vc98\include\oaidl.h(2705) : see declaration of 'GetIDsOfNames'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::Invoke(long,const struct _GUID &,unsigned long,unsigned short,struct tagDISPPARAMS *,struct tagVARIANT *,struct tagEXCEPINFO *,unsigned int *)' :
pure virtual function was not defined
e:\vc98\include\oaidl.h(2712) : see declaration of 'Invoke'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : error C2259: 'CSink' : cannot instantiate abstract class due to following members:
e:\vc study examples\com_atl\fire\client\sink.h(15) : see declaration of 'CSink'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::GetTypeInfoCount(unsigned int *)' : pure virtual function was not defined
e:\vc98\include\oaidl.h(2697) : see declaration of 'GetTypeInfoCount'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )' : pure virtual function was not defined
e:\vc98\include\oaidl.h(2700) : see declaration of 'GetTypeInfo'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *)' : pure virtual function was not defined
e:\vc98\include\oaidl.h(2705) : see declaration of 'GetIDsOfNames'
E:\VC Study Examples\COM_ATL\FIRE\Client\ClientDlg.cpp(214) : warning C4259: 'long __stdcall IDispatch::Invoke(long,const struct _GUID &,unsigned long,unsigned short,struct tagDISPPARAMS *,struct tagVARIANT *,struct tagEXCEPINFO *,unsigned int *)' :
pure virtual function was not defined
e:\vc98\include\oaidl.h(2712) : see declaration of 'Invoke'
Sink.cpp
Generating Code...
Error executing cl.exe.

Client.exe - 2 error(s), 8 warning(s)

thanx in advanceSmile

Dileep

GeneralRe: helpp
Janma
3:25 26 Apr '07  

change the idl file: interface _IAddEvents : IDispatch to interface _IAddEvents : IUnknown


GeneralDCOM problem
mehdi_97
7:13 21 Nov '05  
hi
i could try use this code with DCOM
but i cant

has atladvise function problem with DCOM???
how can change this code for use dcom.
please help me.
thank you
GeneralAny way to make Asynchronous call
prasad.nallam
22:02 18 Aug '05  
Hi ,

Is there any way to make a asynchronous call to the server. the comments given in the article " you can do whatever u want " is confusing .

Please any one help me out by suggesting the solution

Regards
NVK Prasad
GeneralWhy do we need the AddRef,QueryRef stuff in clent side code
urssmiling
4:35 9 Jul '05  
I have a clarification regarding why we need the AddRef(),QueryRef(),Relase() calls to be implemented here in Client side?? Also why cant we call CoCreateInstance() directly.
GeneralMFC-Client using Out-process-server
tagi1
0:44 12 Jul '04  
Hi there,
I have a general question about implementing a Connection-Point to a MFC-Client. I don't have any problems using an in-process-Server. And also using an out-process-server without Connection-Points works fine.
My problem is calling Advise() in the MFC-Client returns with a CONNECT_E_CANNOTCONNECT-Error.
The Reason is, that the Server calls Query-Interface on the Client looking for an IMarshal-Interface. But my client doesn't have any.
How can I add this to my MFC-Client.

Thanks a lot
Tagi
GeneralDetected memory leaks!
Liu Guoping
4:20 11 Jun '04  
Detected memory leaks!
Dumping objects -> E:\Develop\ConnectionCOM\ConnectionClient\ConnectionClientDlg.cpp(155) : {61} normal block at 0x003446F0, 8 bytes long.
Data: < SA > E4 53 41 00 01 00 00 00
Object dump complete.


I trace the Class sink, and find the program never call the deconstruct function. So I think that there may be need some modification in the code.

CSink *pSink;
pSink = new CSink;

if ( NULL == pSink )
{
return E_FAIL;
}

//...

pSink->Release();

I double whether I made some mistakes ...



God knows that I really love you from the bottom of my heart,but I forget to tell you that...
GeneralMATLAB COM
dnqhung
19:34 20 May '04  
any one known how can use Matlab com buider in VC ++?
if no one help me ,i'll Cry Cry hic hic....

dnqhung
Generalhow Extend it for other application?
msh_97
10:07 3 May '04  
this program worked good but
how can i use it for Remote Machine (DCOM)
also i want used exe com insted of dll
but advise function not work correctly
please help me.

GeneralRe: how Extend it for other application?
Rvin
15:09 14 May '04  
Yes, please explain how it could be extended for the DCOM environment.
I did the same thing for a DCOM setup but it didn't work...

THANKS!!!
-arvind
GeneralHELP
diegopansica
5:03 26 Jan '04  
Hi, i'm a new to COM, and i didn't know how to create the mfc project of the client and how to insert the new object Sink, can you explain me step by step please,
i tried to do this but when i create the new object the class viewer has classes that your project hasn't
can you help me ?
Generali am facing problems while tryint to run it using run32dll.exe
Sid_smily
4:18 7 Jan '04  
hi i am using this command from the windows run screen

rundll32.exe "E:\Program Files\Eddie Velasquez\GUIDGen.NET\GuidGenNet.dll" "ShowDialog"

but it is giving an error.. that the specified file can not be found,, please help me in this regard.



Siddique Ahmed
Senior Software Engineer
Telelogix Software
(www.telelogix.com)
Generaldon't generate the .tlh file
manito
18:31 17 Nov '03  
Hi, i follow the example step by step, and i obtain this error:

f:\vc\xxclient\sink.h(34) : error C2065: 'IID__IAddEvents' : undeclared identifier

this, because the .tlh is not generate.
my question is, what i need to do for solve this problem? i download the project and works fine, but i don't idea how fix for myself.

With Best Regards
GeneralRe: don't generate the .tlh file
Anonymous
0:16 25 Mar '04  
hjhjhj


Last Updated 22 Jan 2003 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2010