COM 组件设计与应用(十五)——连接点(vc6.0)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情
作者: 杨老师

下载源代码

一、前言

  上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。

二、原理


图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象)

  看着好复杂呀......呵呵,其实简单的紧:(注1)
1、一个 COM 组件,允许有多个连接点对象(IConnectionPoint)。
   也就是说可以有多个发生“事件”的源头。上图就有3个连接点;
2、管理这些连接点的接口叫“连接点容器”(IConnectionPointContainer)。
   连接点容器接口特别简单,因为只有2个函数,一个是 FindConnectionPoint(),表示查找你想要的连接点;另一个是 EnumConnectionPoints(),表示列出所有的连接点,然后你去选择使用哪个。在实际的应用中,查找法使用最多,占90%,而枚举法使用只占 10%,一般在支持第三方的插件(Plug in)时才使用。(你想写个 IE 的插件吗?我们后面就要讲到啦)
3、每一个连接点,可以被多个客户端的接收器(Sink)连接;
   这个我们已经熟悉啦,还记得我们在上回书中为了管理多个回调接口,使用了 cookie 的方式进行区别吗?!

三、实现组件(一)

1、建立一个工作区(WorkSpace)
2、在工作区中,建立一个 ATL 工程(Project)。示例程序中工程名称叫 Simple15,接受全部默认选项。
3、ClassView 中,执行鼠标右键菜单命令 New Atl Object...,添加 ALT 类。
4、左侧分类 Category 选择 Objects,右侧 Objects 选择 SimpleObject(其实就是默认项目)。
5、名称 Name 卡片中,输入组件名称。示例程序中是 DispConnect。



6、属性 Attributes 卡片中,接口类型选 Dual 双接口。注意一定要选择 Support Connection Points 来支持连接点。



7、ClassView 中,选择接口(IDispConnect),鼠标右键菜单添加函数 Add Method...



8、增加函数。和上回书的程序一样,增加一个接口函数计算加法,但通过连接点接口返回计算结果。



9、下面该增加“事件”函数了。选择事件接口(_IDispConnectEvents),添加函数。



10、该函数用来返回 Add() 函数的计算结果。



11、切换到 FileView 卡片,编译IDL文件。当然你也可以直接编译全部工程。其实编译的目的是为了从IDL文件产生TLB文件,因为 VC 的 IDE 环境只有知道了 TLB 后,才能生成下面的“事件代理类的程序代码”。



12、生成事件代理类程序代码。选择组件类对象(CDispConnect),执行鼠标右键菜单“实现连接点”



13、选择你要让 IDE 帮你生成哪个连接点的代理程序代码。我们这个组件只有一个连接点,那只好选择它了。 (在示例二中,我们需要实现两个连接点,那个时候,你就要选择两个了)



14、到此,VC 的 IDE 终于帮咱们完成了所有的框架,下面该咱们自己写真正的任务代码啦。

STDMETHODIMP CDispConnect::Add(long n1, long n2)
{
	long nVal = n1 + n2;
	Fire_Result( nVal );	// 调用IDE帮我们生成的代理函数代码,发出事件

	return S_OK;
}
15、修正 IDE 产生的代码中的错误。你不用死记硬背错误点,只要编译一下就会报出错误了。一般 VC6 帮我们生成的代码中,有2个地方可能会有BUG。一是打开头文件,找到连接点影射宏,修改如下:
BEGIN_CONNECTION_POINT_MAP(CDispConnect)
	CONNECTION_POINT_ENTRY(DIID__IDispConnectEvents)	// 修改 IID_XXXX 为 DIID_XXXX
END_CONNECTION_POINT_MAP()
  这个错误简直可恨,既然我们使用的是双接口连接点,它生成的代码居然不会判断吗?另一个可能的错误可能发生在代理类中的 Fire_xxxx() 函数中。在示例程序中的 Fire_Result() 函数代码,大家自己去阅读,简单说就是循环地取得每个和自己连接对象(每个cookie表示的对象)的接口指针,(如果是自动化接口,则再取得 IDispatch 接口指针),然后调用事件函数。你不理解它现在没有太大的关系,不过在后面的示例二中,它给我们产生的代码是有错误的,我们需要进行修改。这是后话,待会儿再说。

四、实现调用者(一)

1、建立一个 MFC 工程(Project)。示例程序中的工程名称叫 Use。
2、按照咱们以前所学的知识,添加 #import、AfxOleInit()、......不多浪费口条了。如果你还不会,那么请重新从“ 第四回”再次阅读。 (注2)
3、这里只介绍一下重点部分。我们需要在调用者工程中,增加“接收器”对象。还记得 上回书中的增加“回调接收器”对象的方法吗?上回中,我们的回调接口是从 IUnknown 继承下来的。本回中,由于我们的组件是双接口(Dual)的,连接点也是双接口的,因此这次我们的接收器要从 IDispatch 派生啦。



4、完成 CSink 类的接口函数(虚函数)
STDMETHODIMP CSink::QueryInterface(const struct _GUID &iid,void ** ppv)
{
	*ppv=this;
	return S_OK;
}

ULONG __stdcall CSink::AddRef(void)
{	return 1;	}	// 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

ULONG __stdcall CSink::Release(void)
{	return 0;	}	// 做个假的就可以,因为反正这个对象在程序结束前是不会退出的

STDMETHODIMP CSink::GetTypeInfoCount(unsigned int *)
{	return E_NOTIMPL;	}	// 不用实现,反正也不用

STDMETHODIMP CSink::GetTypeInfo(unsigned int,unsigned long,struct ITypeInfo ** )
{	return E_NOTIMPL;	}	// 不用实现,反正也不用

STDMETHODIMP CSink::GetIDsOfNames(const struct _GUID &,unsigned short ** ,unsigned int,unsigned long,long *)
{	return E_NOTIMPL;	}	// 不用实现,反正也不用

STDMETHODIMP CSink::Invoke(
	long dispID,
	const struct _GUID &,
	unsigned long,
	unsigned short,
	struct tagDISPPARAMS * pParams,
	struct tagVARIANT *,
	struct tagEXCEPINFO *,
	unsigned int *)
{		// 只需要实现这个就足够啦
	switch(dispID)	// 根据不同的dispID,完成不同的回调函数
	{
	case 1:
		......	// 这里就能接收到 COM 发出的事件啦
		break;
	case 2:
		......	// 事件的代号 dispID 其实就是 IDL 文件中的连接点函数的id(n)的号码
		break;
	default:	break;
	}
	return S_OK;
}
、示例(二)

  示例程序中的第2个组件(MultConnect),我们再增加一个连接点( _IDispConnectEvents2 )。这个接口对象负责完成一个时钟,每间隔一定的毫秒就向调用者发出“时钟事件”。增加第二个连接点的方法是要手工修改 IDL 文件
......
library MULTCONNECTLib
{
	importlib("stdole32.tlb");
	importlib("stdole2.tlb");

	...... // 第一个,ATL 框架默认给我们生成的连接点接口描述
	[ // 需要手工增加第二个或更多个连接点
		uuid(F81DB93F-4F63-4A55-8114-A32BC78466D3), // CLSID 可以用 GUIDGEN.EXE 来产生
		helpstring("_IDispConnectEvents2 Interface")
	]
	dispinterface _IDispConnectEvents2
	{
		properties:
		methods:
	};
	[
		uuid(9461BE82-0D64-4E3B-B0DB-2306D1BFE3F0), // 这是示例程序的类型库ID,肯定和你生成的不一样的啦
		helpstring("DispConnect Class")
	]
	coclass DispConnect
	{
		[default] interface IDispConnect;
		[default, source] dispinterface _IDispConnectEvents;
		[source] dispinterface _IDispConnectEvents2; // 别忘了,这里还有一行呢
	};
};
  好了,和前面的方式一样,增加接口函数、编译IDL文件、让IDE帮我们实现代理类代码、输入程序代码、修改框架代码中的BUG。在示例中,我们的事件函数叫 HRESULT Timer([in] VARIANT varData),varData 中传递一个时间类型(VT_DATA)的信息(注3)。下面我们来看一下代理类代码中的错误:
HRESULT Fire_Timer(VARIANT varDate)
{
   CComVariant varResult;
   T* pT = static_cast(this);
   int nConnectionIndex;
   CComVariant* pvars = new CComVariant[1];
   int nConnections = m_vec.GetSize();
		
   for (nConnectionIndex = 0; nConnectionIndex < nConnections; nConnectionIndex++)
   {
    pT->Lock();
    CComPtr
  sp = m_vec.GetAt(nConnectionIndex);
    pT->Unlock();
    IDispatch* pDispatch = reinterpret_cast(sp.p);
    if (pDispatch != NULL)
    {
     VariantClear(&varResult);
     // 原始代码,这里居然是 pvars[0]=&varData?愚蠢之极!只好你自己修改啦
    pvars[0] = varDate;
     DISPPARAMS disp = { pvars, NULL, 1, 0 };
     pDispatch->Invoke(0x1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &disp, &varResult, NULL, NULL);
    }
   }
   delete[] pvars;
   return varResult.scode;
}
  在编写调用者客户端代码方面,如果你需要接收时钟事件,那么可以仿照示例一再从 IDispatch 派生一个时钟接收器。大家下载事例程序代码,里面有丰富的注释信息。

六、小结

  连接点,尤其是双接口的连接点,在远程(DCOM)环境上运行效率是比较低的。如果你只想完成简单的“通知”功能,那么 前一回中的“回调接口”是一个明智的方案,并且可以运行在DCOM环境上。连接点方案当然也很重要,因为微软的许多应用程序(IE、Office......)都支持连接点,并且 ActiveX 只能通过连接点接口提供“事件”功能。所以,咱们还是都掌握为善吧。 善哉 、 善哉 ......

注1:金庸老先生的武侠小说里,总是用“XX 紧”来表示“很 XX”。我也学一学,嘿嘿。
注2:如果看了好几遍,您老人家还不会的话,那只好......先别学了。5555
注3:DATA 类型就是是8字节的double,它的整数部分表示从 1899年12月30日开始的总天数,小数部分表示当天的时间已经渡过了一天的多少分之一。这个时间类型,用VARIANT表示,就是VT_DATE类型,MFC 中用 COleDateTime 表示。示例程序中有对该类型的操作示范。
 
深入掌握COM组件设计应用 本文还有配套的精品资源,点击获取 简介:COMComponent Object Model)组件对象模型是微软开发的一种软件组件技术,它实现了不同程序间代码和资源的共享。本资料包“COM组件设计应用.rar”旨在帮助IT开发者深刻理解COM组件的核心概念、设计原则、应用实践,并掌握关键的技术要点,如注册和反注册、IDispatch接口、聚合、错误处理以及IUnknown... 阅读详情

相关推荐

COM 组件设计应用

COM 组件设计应用(一)——起源及复合文件 COM 组件设计应用(二)——GUID 和 接口 COM 组件设计应用(三)——数据类型 COM 组件设计应用(四)——简单调用组件 COM 组件设计应用(五)——用 ATL 写第一个组件 COM 组件设计应用(六)——用 ATL 写第一个组件 COM 组件设计应用(七)——编译、注册、调用 COM 组件设计应用(八)——实现多接口 COM 组件设计应用(九)——IDispatch 接口 for VC6.0 COM 组件设计应用(十)——IDispatch 接口 for VC.NET COM 组件设计应用(十一)—— IDispatch 及双接口的调用 COM 组件设计应用(十二)——错误异常处理 COM 组件设计应用(十三)——事件和通知(VC6.0) COM 组件设计应用(十四)——事件和通知(vc.net) COM 组件设计应用十五)——连接点(vc6.0) COM 组件设计应用(十六)——连接点(vc.net) COM 组件设计应用(十七)——持续性 COM 组件设计应用(十八)——属性包

COM 组件设计应用之数据类型

 一、前言  上回书介绍了GUID、CLSID、IID和接口的概念。本回的重点是介绍 COM 中的数据类型。咋还不介绍组件程序的设计步骤呀?咳......别着急,别着急!孔子曰:“饭要一口一口地吃”;老子语:“心急吃不了热豆腐”,孙子云:“走一步看一步吧” ...... 先掌握必要的知识,将来写起程序来才会得心应手也:-)  走入正题之前,请大家牢牢记住一条原则:COM 组件是运行在分布式环境中的

天行健 ● 君子以自强不息 1753

COM组件设计应用十五

COM组件设计应用十五连接点(vc6.0)作者:杨老师 下载源代码一、前言  上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。二、原理图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象)  看着好复杂呀......呵呵,其实简单的紧:(注1)1、一个 COM 组件,允许有多个连接点对象(IConnectionPoint)。   也就是说可以有多个发生“事件”的源头。上...

weixin_33696106的博客 222

【亲测免费】 COM组件技术指南及实战示例

COM组件技术指南及实战示例 【下载地址】COM组件技术指南及实战示例 COM组件技术指南及实战示例欢迎阅读这份关于COMComponent Object Model)组件的综合指南实践演示文档 项目地址: https://g...

gitblog_09797的博客 730

COM组件设计应用

COM组件设计应用(二) GUID 和 接口 http://www.vckbase.com/document/viewdoc/?id=1485

作一个独立连续的思考者 726

COM组件设计应用十五连接点(vc6.0)

http://www.vckbase.com/code/downcode.asp?id=2775 下载源码一、前言  上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。二、原理图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象)  看着好复杂呀......呵呵,其实简单的紧:(注1)1、一个 COM 组件,允许有多个连接点对象(ICo

梦回大唐 634

COM组件设计应用十五连接点(vc6.0)

COM组件设计应用十五连接点(vc6.0) 作者:杨老师 下载源代码 一、前言   上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。 二、原理 图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象)   看着好复杂呀......呵呵,其实简单的紧:(注1) 1、一个 COM 组件,允许有多个连接点对象(IConnection

jiht594的专栏 887

COM组件设计应用十五)---连接点(vc6.0)

COM组件设计应用十五连接点(vc6.0) 作者:杨老师 下载源代码 一、前言   上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。 二、原理 图一、连接点组件原理图。左

snail8384的专栏 725

COM组件设计应用十五)--连接点(vc6.0)

 下载源代码一、前言  上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。二、原理图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象)  看着好复杂呀......呵呵,其实简单的紧:(注1)1、一个 COM 组件,允许有多个连接点对象(IConnectionPoint)。   也就是说可以有多个发生“事件”的源头。上图就有3个连接点;2、管理这些连接点的接口叫“连接点容器”

Welcom to zougangx's blog 720

【转载】COM 组件设计应用十五)——连接点(vc6.0)

原文:http://vckbase.com/index.php/wv/1256.html 一、前言 上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。 二、原理 图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象) 看着好复杂呀......呵呵,其实简单的紧:(注1) 1、一个 COM 组件,允许有多个连接点对象(IConnectionPoint)。 也...

weixin_30556959的博客 123

COM 组件设计应用ALL

COM 组件设计应用(十八)——属性包 COM 组件设计应用(十七)——持续性 COM 组件设计应用(十六)——事件和通知(vc.net) COM 组件设计应用十五)——事件和通知(VC6.0) COM 组件设计应用(十四)——事件和通知(vc.net) COM 组件设计应用(十三)——事件和通知(VC6.0) COM 组件设计应用(十二)——错误异常处理

swifty的专栏 1043

COM组件设计应用十五(转载)

COM组件设计应用十五连接点(vc6.0)下载源代码一、前言  上回书介绍了回调接口,在此基础上,我们理解连接点就容易多了。二、原理图一、连接点组件原理图。左侧为客户端,右侧为服务端(组件对象)  看着好复杂呀......呵呵,其实简单的紧:(注1)1、一个 COM 组件,允许有多个连接点对象(IConnectionPoint)。 

WuOu的专栏 923

COM 组件设计应用——杨老师专辑

COM 组件设计应用(十八)——属性包 COM 组件设计应用(十七)——持续性 COM 组件设计应用(十六)——连接点(vc.net) COM 组件设计应用十五)——连接点(VC6.0) COM 组件设计应用(十四)——事件和通知(vc.net):本回示例程序完成的功能是:客户端启动组件(Simple11.IEvent1.1)并得到接口指针 IEvent1 *;调用接口

djimon的专栏 1828
上一篇: COM 组件设计与应用(十三)——事件和通知(VC6.0)
下一篇: COM 组件设计与应用(十六)——连接点(vc.net)
junchieh
博客等级 码龄20年 6粉丝 22原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值