OLE technology topic-Lecture 4: COM connection point event (medium)

Source: Internet
Author: User
Tags call back
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 InterfaceIArithematic, And has a resource intensive method, sayAdd(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 functionExecutionOver(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 callthe client FunctionExecutionOver(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 InterfaceISink, Which has a methodExecutionOver(int result). Now, if the client can pass this interface to the COM object, the COM object can happily callExecutionOver. 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 nowHRESULT 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-IConnectionPointAndIConnectionPointContainer. 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 cballsCoCreateInstanceTo create the COM Object. Once the client has an initial interface, the client can ask the object whether it supports any outgoing interfaces by callingQueryInterfaceForIConnectionPointContainer. 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 callingIConnectionPointContainer::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 thatIConnectionPointInterface pointer and CILSIConnectionPoint::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 pointerIUnknownPassed to the advise method is the pointer of an interface that's defined and implemented in the client exe.

Okay, now let's just strate 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 oneIAddAnd_IAddEvents. The latter is just a proxy class; it is to be implemented on the client side. It came because we tickedConnection_PointsCheck box.

Add a method'Add(int a,int b)'IAddInterface and a method'ExecutionOver(int Result)'To_IAddEventsInterface. The class view will be as shown below.

But because we have selected a dual interface and we do not need all that hassystemic, Let's take out the supportIDispatchBy 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 ininterface _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.

ACProxy_IAddEvetsClass is generated withFire_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 oldIAddInterface add method.

//=====================================================STDMETHODIMP CAdd::Add(int a, int b){// TODO: Add your implementation code hereSleep(2000);   // to simulate a long process//OK, process over now; let's notify the clientFire_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 createCSinkClass that derives form_IAddEvents. You can use the Class Wizard for it. You have to supply the header where_IAddEventsInterface 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_IAddEventsInterface. (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 firstExecutionOver:

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 onSendToServerButton click event, we shall do the last bit of coding.

#include "Sink.h"          // for our CSink class#include <atlbase.h>       // for ATL smart pointersvoid CConnectionClientDlg::OnSendToServer()     //SendToServer button click event{UpdateData(1);HRESULT hr;//call CoInitialize for COM initialisationhr =CoInitialize(NULL);if(hr != S_OK)  return -1;// create an instance of the COM objectCComPtr<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 interfacepCPC->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  //CSinkclasshr = 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 ushr = 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 serverpCP->Release();  return hr;}

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

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.