博文

COM组件设计与应用(十八)(2006-02-28 12:20:00)

摘要:属性包

作者:杨老师 下载源代码

一、前言
  书接上回,本回着落在介绍属性包 IPersistPropertyBag 接口的实现方法和调用方式。属性包,是以“名称 - 值”的方式提供组件持续性的支持,而“名称 - 值”恰恰又适合于用文本方式来表现。下面的片段是在 HTML 中插入 Microsoft MonthView Control ActiveX 控件后的样式:<object classid="clsid:232E456A-87C3-11D1-8BE3-0000F8754DA1" id="MonthView1"> <param name="_ExtentX" value="9393"> <param name="_ExtentY" value="4974"> <param name="_Version" value="393216"> <param name="ForeColor" value="0"> <param name="MaxSelCount" value="7"> <param name="MonthColumns" value="1"> <param name="CurrentDate" value="38632"> <param name="MaxDate" value="2958465"> <param name="MinDate" value="-53688"> </object>以文本方式保存组件属性,比较直观、容易修改,上面 HTML 示例中的 <param name="属性名" value="值"> 就很清晰。下面开始介绍如何在组件中实现 IPersistPropertyBag 接口。

二、组件的实现
(1)vc6.0 开发步骤
1、建立一个工作空间(WorkSpace)。
2、在这个工作空间中,建立 ATL 工程,示例程序工程为 Simple18。
3、增加 ATL 对象类,默认全部选项。示例程序中的 ATL 对象短名称是 Property。
4、增加一些属性。在以前的章回中......

阅读全文(3397) | 评论:1

COM 组件设计与应用(十七)(2006-02-28 12:18:00)

摘要:持续性

作者:杨老师 下载源代码

一、前言
  我们写程序,经常需要实现这样的需求:
例一、程序运行产生一个窗口,用户关闭的时候需要记录窗口的位置,以便下次运行时保持位置不变;
例二、由于程序运行时间很长,今天执行一部分,明天继续执行。那么在下次运行前要恢复前次的状态;
... ... ... ...

智慧的老师:以上这些需求,如何实现呢?
懵懂的学生:这个简单,只要在程序退出前提取必要的信息保存到文件中,下次运行时再从文件中读出来,设置一下就OK了。
智慧的老师:恩,不错,这位同学的思想值得表扬。
懵懂的学生:不好意思,这都要感谢老师的栽培,我对您的景仰如滔滔江水......
智慧的老师:别臭P了,我话还没有说完那......如果你需要提取和保存的信息很多,结构很复杂......怎么办?
懵懂的学生:也好办,我设计一个结构来记录这些信息。
智慧的老师:恩......不错。但如果这些信息提供方是别人写的模块,并且随着版本的不同还经常变化,你怎么办?
懵懂的学生:... ...
智慧的老师:解决这些问题的方法是---持续性。

二、原理
  持续性,也叫永久性。组件方提供 IPersistXXX 接口,调用者(容器)提供存储介质,比如文件啦、内存啦、注册表啦、流啦、文本啦......啦啦拉。需要保存的时候,调用者通过 IPersistXXX::Save() 接口函数让组件去自己存储属性信息,而调用者根本不用关心存储格式和存储内容;需要还原状态的时候,调用者打开存储介质,然后同样调用 IPersistXXX::Load() 接口函数让组件自己去读取属性信息并完成初始化的设置。
  目前,微软定义了如下各种类型的持续性接口,足够满足你的需求了。我们只要在自己写的组件中实现其中一个或几个持续性接口,那么调用者就可以按照统一的方式和我们的组件协商完成属性信息的保存和状态还原了。
  持续性接口 简要说明 IPersist    所有持续性接口的根,下面的接口大多从它派生出来。这个接口很简单,只有一个函数 GetClassID()它返回组件的 CLSID 号,以便调用者能保存这个号为将来 CoCre......

阅读全文(3315) | 评论:0

COM组件设计与应用(十六)(2006-02-27 14:42:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1539 连接点(vc.net)

作者:杨老师 下载源代码

一、前言

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

二、原理


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

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

三、实现组件(一)

1、建立一个空白解决方案。
2、在解决方案中,新增 ATL 项目。示例程序中项目名称叫 Simple16, 注意不要选择“属性化编程”方式。
3、添加 ATL 类。选择 “ATL 的简单对象”。
4、名称卡片中,输入组件名称。示例程序中是 DispConnect。



5、选项卡片中,接口类型选双接口。注意一定要选择“连接点”。



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





7、下面该增加“事件”函数了。选择......

阅读全文(2997) | 评论:0

COM组件设计与应用(十五)(2006-02-27 14:41:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1538 连接点(vc6.0)

作者:杨老师 下载源代码

一、前言

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

二、原理


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

  看着好复杂呀......呵呵,其实简单的紧:(注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 卡片中,接口类型选......

阅读全文(3173) | 评论:0

COM组件设计与应用(十四)(2006-02-25 13:57:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1526 事件和通知(vc.net)

作者:杨老师 下载源代码

一、前言
  我的 COM 组件运行时产生一个窗口,当用户双击该窗口的时候,我需要通知调用者;
  我的 COM 组件用线程方式下载网络上的一个文件,当我完成任务后,需要通知调用者;
  我的 COM 组件完成一个钟表的功能,当预定时间到达的时候,我需要通知调用者;
  ... ... ... ...
  本回书开始话说 COM 的事件、通知、连接点......这些内容比较多,我分两次(共四回)来介绍。

二、通知的方法
  当程序甲方内部发生了某个事件的时候,需要通知乙方,无非使用几个方法:
  通知方式 简单说明 评论 直接消息 PostMessage()
PostThreadMessage() 向窗口或线程发个消息 你什么时候执行我就不管啦 SendMessage() 马上执行消息响应函数 不执行完消息处理函数不会返回 SendMessage(WM_COPYDATA...) 发消息的同时,还可以带过去一些自定义的数据 比较常用,所以单独列了出来 间接消息 InvalidateRect()
SetTimer()
...... 被调用的函数会发送相关的一些消息 这样的函数太多了 回调函数 GetOpenFileName()...... 当用户改变文件选择的时候,执行回调函数 嗨!哥们,这是我的电话,有事就言语一声。   在 COM 的时代,以上这些方法就基本上不能玩转了,因为...您想呀 COM 组件是运行在分布式环境中的,地球另一边计算机上运行的组件,怎么可能给你的窗口发消息那?当然不能!(但话又说回来,对于 ActiveX 这样只能在本地运行的组件,当然也可以发送窗口消息的啦。)
  回调函数的方式,是设计 COM 通知方法的基础。回调函数,本质上是预先把某一函数的指针告诉我,当我有必要的时候,就直接呼叫该函数了,而这个回调函数做了什么,怎么做的,我是根本不关心的。好了,问你个问题:啥是 COM 的接口?接口其实就是......

阅读全文(3138) | 评论:0

COM组件设计与应用(十三)(2006-02-25 13:56:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1525 事件和通知(VC6.0)

作者:杨老师 下载源代码

一、前言
  我的 COM 组件运行时产生一个窗口,当用户双击该窗口的时候,我需要通知调用者;
  我的 COM 组件用线程方式下载网络上的一个文件,当我完成任务后,需要通知调用者;
  我的 COM 组件完成一个钟表的功能,当预定时间到达的时候,我需要通知调用者;
  ... ... ... ...
  本回书开始话说 COM 的事件、通知、连接点......这些内容比较多,我分两次(共四回)来介绍。

二、通知的方法
  当程序甲方内部发生了某个事件的时候,需要通知乙方,无非使用几个方法:
  通知方式 简单说明 评论 直接消息 PostMessage()
PostThreadMessage() 向窗口或线程发个消息 你什么时候执行我就不管啦 SendMessage() 马上执行消息响应函数 不执行完消息处理函数不会返回 SendMessage(WM_COPYDATA...) 发消息的同时,还可以带过去一些自定义的数据 比较常用,所以单独列了出来 间接消息 InvalidateRect()
SetTimer()
...... 被调用的函数会发送相关的一些消息 这样的函数太多了 回调函数 GetOpenFileName()...... 当用户改变文件选择的时候,执行回调函数 嗨!哥们,这是我的电话,有事就言语一声。   在 COM 的时代,以上这些方法就基本上不能玩转了,因为...您想呀 COM 组件是运行在分布式环境中的,地球另一边计算机上运行的组件,怎么可能给你的窗口发消息那?当然不能!(但话又说回来,对于 ActiveX 这样只能在本地运行的组件,当然也可以发送窗口消息的啦。)
  回调函数的方式,是设计 COM 通知方法的基础。回调函数,本质上是预先把某一函数的指针告诉我,当我有必要的时候,就直接呼叫该函数了,而这个回调函数做了什么,怎么做的,我是根本不关心的。好了,问你个问题:啥是 COM 的接口?接口其实就是一......

阅读全文(2996) | 评论:0

COM组件设计与应用(十二)(2006-02-23 16:33:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1520 错误与异常处理

作者:杨老师 下载源代码

一、前言
  程序设计中,错误处理必不可少,而且通常要占用很大的篇幅。本回书着落在 COM 中的错误(异常)的处理方法。
  在组件程序中,如果遇到错误,一般有两个方式进行处理。

二、简单返回
  对于比较简单的错误,直接返回表示错误原因的 HRESULT。比如下面几个就是常见的错误值:
  E_INVALIDARG 0x80070057 参数错误 E_OUTOFMEMORY 0x8007000E 内存错误 E_NOTIMPL 0x80004001 未实现 E_POINTER 0x80004003 无效指针 E_HANDLE 0x80070006 无效句柄 E_ABORT 0x80004004 终止操作 E_ACCESSDENIED 0x80070005 拒绝访问 E_NOINTERFACE 0x80004002 不支持接口   另外,你还可以返回自己构造 HRESULT 错误值。方法是使用宏 MAKE_HRESULT(sev,fac,code)
  参数 含义 值(二进制) sev 严重程度 成功 00 成功,但有一些报告信息 01 警告 10 错误 11 fac 设备信息 FACILITY_AAF 00000010010 FACILITY_ACS 00000010100 FACILITY_BACKGROUNDCOPY 00000100000 FACILITY_CERT 00000001011 FACILITY_COMPLUS 00000010001 FACILITY_CONFIGURATION 00000100001 FACILITY_CONTROL 00000001010 FACILITY_DISPATCH 00000000010 FACILITY_DPLAY 00000010101 FACILITY_HTT......

阅读全文(2979) | 评论:0

COM 组件设计与应用(十一)(2006-02-23 16:32:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1518 IDispatch 及双接口的调用

作者:杨老师 下载源代码

一、前言
    前段时间,由于工作比较忙,没有能及时地写作。其间收到了很多网友的来信询问和鼓励,在此一并表示感谢。咳......我也需要工作来养家糊口呀......
    上回书介绍了两种方法来写自动化(IDispatch)接口的组件程序,一是用 MFC 方式编写“纯粹”的 IDispatch 接口;二是用 ATL 方式编写“双接口”的组件。 二、IDispatch 接口和双接口
    使用者要想调用普通的 COM 组件功能,必须要加载这个组件的类型库(Type library)文件 tlb(比如在 VC 中使用 #import)。然而,在脚本程序中,由于脚本是被解释执行的,所以无法使用加载类型库的方式进行预编译。那么脚本解释器如何使用 COM 组件那?这就是自动化(IDispatch)组件大显身手的地方了。IDispatch 接口需要实现4个函数,调用者只通过这4个函数,就能实现调用自动化组件中所有的函数。这4个函数功能如下:
  HRESULT GetTypeInfoCount(
    [out] UINT * pctinfo) 组件中提供几个类型库?当然一般都是一个啦。
但如果你在一个组件中实现了多个 IDispatch 接口,那就不一定啦(注1) HRESULT GetTypeInfo(
    [in] UINT iTInfo,
    [in] LCID lcid,
    [out] ITypeInfo ** ppTInfo) 调用者通过该函数取得他想要的类型库。
幸好,在 99% 的情况下,我们都不用关心这两个函数的实现,因为 MFC/ATL 都帮我们完成了默认的一个实现,如果是自己完成函数代码,甚至可以直接返回 E_NOTIMPL 表示没有实现。(......

阅读全文(2875) | 评论:0

COM组件设计与应用(十)(2006-02-21 20:15:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1507 IDispatch 接口 for vc.net

作者:杨老师 下载源代码 一、前言
    终于写到了第十回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常用、非常有用、非常精彩的一个 COM 功能。由于 WORD、EXCEL 等 OFFICE 软件提供了“宏”的功能,就连我们使用的VC开发环境也提供了“宏”功能,更由于 HTML、ASP、JSP 等都要依靠脚本(Script)的支持,更体现出了自动化接口的重要性。
    如果你使用 vc6.0 的开发环境,请阅读前一回。
    如果你使用 vc.net 2003,请继续...... 二、IDispatch接口
    如果是编译型语言,那么我们可以让编译器在编译的时候装载类型库,也就是装载接口的描述。在第七回文章当中,我们分别使用了 #include 方法和 #import 方法来实现的。装载了类型库后,编译器就知道应该如何编译接口函数的调用了---这叫“前绑定”。但是,如果想在脚本语言中使用组件,问题就大了,因为脚本语言是解释执行的,它执行的时候不会知道具体的函数地址,怎么办?自动化接口就为此诞生了---“后绑定”。
    自动化组件,其实就是实现了 IDispatch 接口的组件。IDispatch 接口有4个函数,解释语言的执行器就通过这仅有的4个函数来执行组件所提供的功能。IDispatch 接口用 IDL 形式说明如下:(注1)[ object, uuid(00020400-0000-0000-C000-000000000046), // IDispatch 接口的 IID = IID_IDispatch pointer_default(unique) ] interface IDispatch : IUnknown { typedef [unique] IDispatch * LPDISPATCH; // ......

阅读全文(2840) | 评论:0

COM组件设计与应用(九)(2006-02-21 20:14:00)

摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1506 IDispatch 接口 for vc6.0

作者:杨老师 下载源代码 一、前言
    终于写到了第九回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常用、非常有用、非常精彩的一个 COM 功能。由于 WORD、EXCEL 等 OFFICE 软件提供了“宏”的功能,就连我们使用的VC开发环境也提供了“宏”功能,更由于 HTML、ASP、JSP 等都要依靠脚本(Script)的支持,更体现出了自动化接口的重要性。
    如果你使用 vc6.0 的开发环境,请继续阅读。
    如果你使用 vc.net 2003,请阅读下一回。 二、IDispatch接口
    如果是编译型语言,那么我们可以让编译器在编译的时候装载类型库,也就是装载接口的描述。在第七回文章当中,我们分别使用了 #include 方法和 #import 方法来实现的。装载了类型库后,编译器就知道应该如何编译接口函数的调用了---这叫“前绑定”。但是,如果想在脚本语言中使用组件,问题就大了,因为脚本语言是解释执行的,它执行的时候不会知道具体的函数地址,怎么办?自动化接口就为此诞生了---“后绑定”。
    自动化组件,其实就是实现了 IDispatch 接口的组件。IDispatch 接口有4个函数,解释语言的执行器就通过这仅有的4个函数来执行组件所提供的功能。IDispatch 接口用 IDL 形式说明如下:(注1)[ object, uuid(00020400-0000-0000-C000-000000000046), // IDispatch 接口的 IID = IID_IDispatch pointer_default(unique) ] interface IDispatch : IUnknown { typedef [unique] IDispatch * LPDISPATCH; // 转定义 ......

阅读全文(2429) | 评论:0