<!-- Download Links -->
IntroductionThis 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 So, when the COM object is finished with the task, it calls the client
function How Does the COM Object Know How to Call ExecutionOver??Imagine that the client exposes an interface Collapse | Copy Code //=================================================== 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 - Both the interfaces are shown below. Collapse | Copy Code 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 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 Okay, now let's illustrate the whole thing by a practical example.
Note the classes generated in the class view. You will find one Add a method ' But because we have selected a dual interface and we do not need all that
hassle, let's take out the support for Collapse | Copy Code //=========================================================== // 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 Collapse | Copy Code //===================================================== 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 SideCreate a new MFC AppWIzard(exe) Dialog based project - Now comes the main part. We create a Collapse | Copy Code #include "ConnectionCOM.h" #import "ConnectionCOM.tlb" named_guids raw_interfaces_only Now we have the additional task of implementing each method defined in the So, let's implement the first Collapse | Copy Code STDMETHODIMP ExecutionOver(int Result) { CString strTemp; strTemp.Format("The result is %d", Result); AfxMessageBox(strTemp); return S_OK;; }; Now comes QueryInterface, AddRef, and Release. Collapse | Copy Code 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 Collapse | Copy Code #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. |
|
来自: benladeng45 > 《COM》