易语言教程_易语言源码_易语言写挂_易语言论坛_看流星社区

 找回密码
 注册
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
零基础辅助入门教学 原创 高清 专业课程售后(每日解答)
赞助广告位 请点击这里联系站长 QQ20209081
赞助广告位 请点击这里联系站长 QQ20209081
赞助广告位 请点击这里联系站长 QQ20209081
查看: 58|回复: 0

VC编写COM连接点事件

[复制链接]
发表于 2017-6-3 14:26:06 | 显示全部楼层 |阅读模式
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 **)&amp;pCPC);
if(!SUCCEEDED(hr))
{
return ;
}
IConnectionPoint *pCP;
hr = pCPC->FindConnectionPoint(DIID__IFunEvent,&amp;pCP);
if ( !SUCCEEDED(hr) )
{
return ;
}
pCPC->Release();

IUnknown *pSinkUnk;
CSkin *pSink = new CSkin();
hr = pSink->QueryInterface(IID_IUnknown,(void **)&amp;pSinkUnk);
DWORD dwAdvise;
hr = pCP->Advise(pSinkUnk,&amp;dwAdvise);//接收器与连接点建立关联

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

CoUninitialize();

参考资料:
COM Connection Points By Alex C. Punnen:
http://www.codeproject.com/KB/COM/connectionpoint.aspx
COM组件设计与应用
http://www.vckbase.com/document/viewdoc/?id=1525
VC编写与调用COM连接点代码
http://download.csdn.net/source/3437607
您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|手机版|Archiver|看流星社区  |网站地图

GMT+8, 2017-6-26 23:46 易语言论坛 易语言导航

Powered by 看流星社区 X3.2

©2011-2016 最好的辅助编程技术论坛

快速回复 返回顶部 返回列表