基于VC编写COM连接点事件的分析介绍

 更新时间:2013年05月17日 12:30:07   作者:  
本篇文章是对VC编写COM连接点事件进行了详细的分析介绍,需要的朋友参考下

COM 中的典型方案是让客户端对象实例化服务器对象,然后调用这些对象。然而,没有一种特殊机制的话,这些服务器对象将很难转向并回调到客户端对象。COM 连接点便提供了这种特殊机制,实现了服务器和客户端之间的双向通信。使用连接点,服务器能够在服务器上发生某些事件时调用客户端。

原理如下图:

有了连接点,服务器可通过定义一个接口来指定它能够引发的事件。服务器上引发事件时,要采取操作的客户端会向服务器进行自行注册。随后,客户端会提供服务器所定义接口的实现。

客户端可通过一些标准机制向服务器进行自行注册。COM 为此提供了 IConnectionPointContainer 和 IConnectionPoint 接口。

COM 连接点服务器的客户端可用 C++ 和 C# 托管代码来编写。C++ 客户端会注册一个类的实例,该类提供了接收器接口的实现。托管客户端会注册单个事件的委托,因而会按每个事件通知方法创建单个接收器,具体参考C#的互操作部分内容。

一、连接点程序编写
1、使用ATL建立组件程序。
2、添加ATL     SIMPLE     OBJECT,支持连接点事件。
注:如果当时没有现在连接点事件,可以在.idl文件中手动添加。比如

复制代码 代码如下:

 [
       uuid(57CCB7A5-F3B6-4990-91CD-33A82E1AAA46),
       helpstring("IFunEvent dispinterface")
    ]
    dispinterface _IFunEvent
    {
       properties:
           //  事件接口没有任何属性
       methods:
           [id(1), helpstring("方法OnResult")] HRESULT OnResult([out,retval] LONG* retval);
           [id(2), helpstring("方法OnType")] HRESULT OnType([in] LONG nType);
}

3、因为支持连接点事件,这样将会自动生成一个     _XXXEVENT源接口。我们在其中增加想要触发的方法。
4、选择组件下的事件对象,弹出对话框选择添加方法。可以继续添加多个方法…
5、实现方法(其实组件里只是做方法的申明,客户调用时才实现这些方法)。实现时选中组件/类,按右键,在弹出菜单中选中implement     connection....
就会产生CProxy_xxxEvent类,里面有Fire函数的实现,都是自动生成的。
6、完成组件的其他接口函数。
组件的连接点编写比较简单,关键是如何在客户端实现事件监听与接收。在.NET下很容易实现。但在VC中比较繁琐。
二、连接点客户端实现(VC)
1、包含“工程_i.h”头文件,引入“工程.tlb”ole库文件。比如:
#include "ATLDemo_i.h"
#import "ATLDemo.tlb" named_guids raw_interfaces_only
2、创建一个类:由_IXXXEvent派生过来。(XXX为实际事件名)
实现类各个虚函数重载,如果_IXXXEvent是IUnkown接口只需要重载QueryInterface、AddRef、Release函数;如果_IXXXEvent是双向接口需要重载实现IUnkown接口三个函数和IDispatch接口四个函数。
实现事件功能,通过函数、用SINK_ENTRY_INFO实现事件的映射、Invoke函数里面实现(通过事件ID)三种方法之一来实现。
用SINK_ENTRY_INFO实现事件的映射
如:
复制代码 代码如下:

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

我在组件中定义了一个Msf函数,所以在这里对其进行消息隐射。然后实现Msg方法。
3、如何调用
3.1使用工程支持COM,使用afxoleinit或者CoInitialize/Un CoInitialize
3.2得到组件接口
3.3得到连接点容器,查找连接点。
3.4利用Advise将一个监听对象传给组件,这样当事件发生的时候事件就会响应。在不使用时,通过UnAdvise来断开连接点事件。同时也利用AfxConnectionAdvice将监听对象传给组件接口。
3.5 释放资源。
具体代码如下:
复制代码 代码如下:

#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(
       /* [out] */ __RPC__out UINT *pctinfo)
    {
       return S_OK;
    }

    HRESULT STDMETHODCALLTYPE GetTypeInfo(
       /* [in] */ UINT iTInfo,
       /* [in] */ LCID lcid,
       /* [out] */ __RPC__deref_out_opt ITypeInfo **ppTInfo)
    {
       return S_OK;
    }

     HRESULT STDMETHODCALLTYPE GetIDsOfNames(
       /* [in] */ __RPC__in REFIID riid,
       /* [size_is][in] */ __RPC__in_ecount_full(cNames) LPOLESTR *rgszNames,
       /* [range][in] */ UINT cNames,
       /* [in] */ LCID lcid,
       /* [size_is][out] */ __RPC__out_ecount_full(cNames) DISPID *rgDispId)
    {
       return S_OK;
    }

     /* [local] */ HRESULT STDMETHODCALLTYPE Invoke(
       /* [in] */ DISPID dispIdMember,
       /* [in] */ REFIID riid,
       /* [in] */ LCID lcid,
       /* [in] */ WORD wFlags,
       /* [out][in] */ DISPPARAMS *pDispParams,
       /* [out] */ VARIANT *pVarResult,
       /* [out] */ EXCEPINFO *pExcepInfo,
       /* [out] */ UINT *puArgErr)
    {
       switch(dispIdMember) // 根据不同的dispIdMember,完成不同的回调函数,事件函数的ID编号
       {
       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)
{
}

实现部分:
复制代码 代码如下:

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);//接收器与连接点建立关联

    LONG c = 0;
    pFun->Add(1,5,&c);
    //pCP->Unadvise(dwAdvise) //断开连接点事件
    pCP->Release();
    pFun.Release();

    CoUninitialize();

相关文章

  • C语言数组元素的循环移位方法

    C语言数组元素的循环移位方法

    今天小编就为大家分享一篇C语言数组元素的循环移位方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-07-07
  • 一些C语言中字符串的算法问题解决实例小结

    一些C语言中字符串的算法问题解决实例小结

    这篇文章主要介绍了一些C语言中字符串的算法问题解决实例小结,包括将字符串转化为int类型的数及旋转字符串等操作,需要的朋友可以参考下
    2016-03-03
  • C语言:自定义类型详解

    C语言:自定义类型详解

    这篇文章主要介绍了C语言自定义函数详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-09-09
  • C语言中字符串的内存地址操作的相关函数简介

    C语言中字符串的内存地址操作的相关函数简介

    这篇文章主要介绍了C语言中字符串的内存地址操作的相关函数,包括bcopy()函数和bzero()函数以及bcmp()函数,需要的朋友可以参考下
    2015-08-08
  • C++ 实现优先队列的简单实例

    C++ 实现优先队列的简单实例

    这篇文章主要介绍了C++ 实现优先队列的简单实例的相关资料,希望通过本文能帮助大家实现优先队列,需要的朋友可以参考下
    2017-08-08
  • C++实现转置矩阵的循环

    C++实现转置矩阵的循环

    大家好,本篇文章主要讲的是C++实现转置矩阵的循环,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2022-01-01
  • C++ Qt开发之运用QJSON模块解析数据

    C++ Qt开发之运用QJSON模块解析数据

    JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它易于人阅读和编写,也易于机器解析和生成,本文主要介绍了Qt如何运用QJson组件的实现对JSON文本的灵活解析功能,需要的可以参考下
    2024-01-01
  • 图文详解C语言位运算基础知识

    图文详解C语言位运算基础知识

    这篇文章主要以图文结合的方式为大家详细介绍了C语言位运算基础知识,感兴趣的小伙伴们可以参考一下
    2016-07-07
  • C语言输出唯一的子串

    C语言输出唯一的子串

    这篇文章主要介绍了C语言输出唯一的子串,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2021-12-12
  • DEV C++源码编译后控制台输出中文乱码问题解决

    DEV C++源码编译后控制台输出中文乱码问题解决

    本文主要介绍了DEV C++源码编译后控制台输出中文乱码问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01

最新评论