VC based COM connection point event analysis

  • 2020-04-01 23:40:28
  • OfStack

A typical scenario in COM is to have client objects instantiate server objects and then invoke those objects. However, without a special mechanism, these server objects will be difficult to redirect and callback to client objects. COM join points provide this special mechanism for two-way communication between server and client. Using join points, the server is able to invoke the client when certain events occur on the server.

The principle is as follows:

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201305/201305171218449.gif ">

< img Alt = "" border = 0 SRC =" / / files.jb51.net/file_images/article/201305/2013051712184410.gif ">

With connection points, the server can specify the events it can raise by defining an interface. When an event is raised on the server, the client taking the action registers itself with the server. The client then provides an implementation of the interface defined by the server.

The client can register itself with the server through some standard mechanisms. COM provides IConnectionPointContainer and IConnectionPoint interface.

COM connection point server client can be written in C++ and C# managed code. The C++ client registers an instance of a class that provides an implementation of the receiver interface. The managed client registers a delegate for a single event, so a single sink is created for each event notification method, referring to the interop section of C#.

One, connection point programming
1. Use ATL to create component programs.
2. Add ATL         SIMPLE         OBJECT, which supports join point events.
Note: if the current join point event was not available then, you can add it manually in the.idl file. Such as


 [
       uuid(57CCB7A5-F3B6-4990-91CD-33A82E1AAA46),
       helpstring("IFunEvent dispinterface")
    ]
    dispinterface _IFunEvent
    {
       properties:
           //& have spent The event interface has no properties
       methods:
           [id(1), helpstring(" methods OnResult")] HRESULT OnResult([out,retval] LONG* retval);
           [id(2), helpstring(" methods OnType")] HRESULT OnType([in] LONG nType);
}

3. Since join point events are supported, one will be generated automatically         _XXXEVENT source interface. We add methods to it that we want to trigger.
4. Select the event object under the component and select the add method in the pop-up dialog box. You can continue to add multiple methods...
5. Implement methods (in fact, components only declare methods and implement them when the client calls them). To implement, select the component/class, right-click, and select implement from the pop-up menu         The connection...
The CProxy_xxxEvent class, which has an implementation of the Fire function, is generated automatically.
6. Complete other interface functions of the component.
The component's join point is relatively simple to write, the key is how to implement the event monitoring and receiving in the client side. It's easy to implement under.net. But in VC it's a little more complicated.
Ii. Connection point client implementation (VC)
1. Include the "project _i.h" header file and introduce the "project. TLB" ole library file. Such as:
# include "ATLDemo_i. H"
# import "ATLDemo. TLB" named_guids raw_interfaces_only
2, create a class: derived from _IXXXEvent. (XXX is the actual event name)
If _IXXXEvent is IUnkown interface, just reload QueryInterface, AddRef, Release; If _IXXXEvent is a two-way interface, it needs to overload to implement three functions of IUnkown interface and four functions of IDispatch interface.
The event function is implemented through functions, the event mapping with SINK_ENTRY_INFO, and one of three methods implemented in the Invoke function (by event ID).
Implement the event mapping with SINK_ENTRY_INFO
Such as:

BEGIN_SINK_MAP(CEventSink)
    SINK_ENTRY_INFO(1,DIID__INew01Events,DISPID_MSG,Msg,&MsgInfo)
END_SINK_MAP() 

I've defined an Msf function in the component, so I'm going to message it here. Then implement the Msg method.
3. How to invoke
3.1 using engineering support COM, using afxoleinit or CoInitialize/Un CoInitialize
3.2 get the component interface
3.3 get the join point container and find the join point.
3.4 pass a listener object to the component with Advise so that the event responds when it occurs. When not used, disconnect the join point event with UnAdvise. AfxConnectionAdvice is also used to pass the listener object to the component interface.
3.5 release resources.
The specific code is as follows:

#pragma once

#include "ATLDemo_i.h"
#import "ATLDemo.tlb" named_guids raw_interfaces_only

class CSkin : public _IFunEvent
{
public:
    CSkin(void);
    ~CSkin(void);
private:
    DWORD       m_dwRefCount;
public:
    STDMETHODIMP Fire_OnType( LONG nType)
    {
       CString    strTemp;
       strTemp.Format(_T("The result is %d"), nType);
       AfxMessageBox(strTemp);
       return S_OK;;
    }
    HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppvObject)
    {
       if (iid == DIID__IFunEvent)
       {
           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;
    }

    HRESULT STDMETHODCALLTYPE GetTypeInfoCount(
        __RPC__out UINT *pctinfo)
    {
       return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetTypeInfo(
        UINT iTInfo,
        LCID lcid,
        __RPC__deref_out_opt ITypeInfo **ppTInfo)
    {
       return S_OK;
    }

     HRESULT STDMETHODCALLTYPE GetIDsOfNames(
        __RPC__in REFIID riid,
        __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
        UINT cNames,
        LCID lcid,
        __RPC__out_ecount_full(cNames) DISPID *rgDispId)
    {
       return S_OK;
    }

      HRESULT STDMETHODCALLTYPE Invoke(
        DISPID dispIdMember,
        REFIID riid,
        LCID lcid,
        WORD wFlags,
        DISPPARAMS *pDispParams,
        VARIANT *pVarResult,
        EXCEPINFO *pExcepInfo,
        UINT *puArgErr)
    {
       switch(dispIdMember) //According to the different dispIdMember, complete the different callback function, the ID number of the event function
       {
       case 2:
           {
              // 1st param : [in] long lValue.
              VARIANT varlValue;
              long lValue = 0;
              VariantInit(&varlValue);
              VariantClear(&varlValue);
              varlValue = (pDispParams->rgvarg)[0];
              lValue = V_I4(&varlValue);
              Fire_OnType(lValue);
           }
           break;
       default:   break;
       }

       return S_OK;
    }
};

#include "StdAfx.h"
#include "Skin.h"

CSkin::CSkin(void)
{
     m_dwRefCount =0;
}

CSkin::~CSkin(void)
{
}

Implementation part:

CoInitialize(NULL);

    CComPtr<IFun> pFun;
    HRESULT hr = pFun.CoCreateInstance(CLSID_Fun);
    if(hr!=S_OK)
    {
       return ;
    }

    IConnectionPointContainer *pCPC;
    hr = pFun->QueryInterface(IID_IConnectionPointContainer,(void **)&pCPC);
    if(!SUCCEEDED(hr))
    {
       return ;
    }
    IConnectionPoint *pCP;
    hr = pCPC->FindConnectionPoint(DIID__IFunEvent,&pCP);
    if ( !SUCCEEDED(hr) )
    {
       return ;
    }
    pCPC->Release();

    IUnknown *pSinkUnk;
    CSkin *pSink = new CSkin();
    hr = pSink->QueryInterface(IID_IUnknown,(void **)&pSinkUnk);
    DWORD dwAdvise;
    hr = pCP->Advise(pSinkUnk,&dwAdvise);//The sink is associated with the join point

    LONG c = 0;
    pFun->Add(1,5,&c);
    //pCP->Unadvise(dwAdvise) // Disconnect the connection point event 
    pCP->Release();
    pFun.Release();

    CoUninitialize();


Related articles: