手机
当前位置:查字典教程网 >编程开发 >C语言 >基于VC编写COM连接点事件的分析介绍
基于VC编写COM连接点事件的分析介绍
摘要:COM中的典型方案是让客户端对象实例化服务器对象,然后调用这些对象。然而,没有一种特殊机制的话,这些服务器对象将很难转向并回调到客户端对象。...

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

原理如下图:

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

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

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

客户端可通过一些标准机制向服务器进行自行注册。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();

【基于VC编写COM连接点事件的分析介绍】相关文章:

关于STL中的map容器的一些总结

基于c语言知识点的补遗介绍

变量定义与声明的区别详细解析

关于STL中vector容器的一些总结

基于C语言指令的深入分析

关于C语言函数strstr()的分析以及实现

关于C++内存中字节对齐问题的详细介绍

基于堆的基本操作的介绍

基于C++中常见内存错误的总结

C++类中的常量介绍

精品推荐
分类导航