原文:http://vckbase.com/index.php/wv/1265.html

一、前言

书接上回,本回着落在介绍属性包 IPersistPropertyBag 接口的实现方法和调用方式。属性包,是以“名称 - 值”的方式提供组件持续性的支持,而“名称 - 值”恰恰又适合于用文本方式来表现。下面的片段是在 HTML 中插入 Microsoft MonthView Control ActiveX 控件后的样式:

  1. <object classid="clsid:232E456A-87C3-11D1-8BE3-0000F8754DA1" id="MonthView1">
  2. <param name="_ExtentX" value="9393">
  3. <param name="_ExtentY" value="4974">
  4. <param name="_Version" value="393216">
  5. <param name="ForeColor" value="0">
  6. <param name="MaxSelCount" value="7">
  7. <param name="MonthColumns" value="1">
  8. <param name="CurrentDate" value="38632">
  9. <param name="MaxDate" value="2958465">
  10. <param name="MinDate" value="-53688">
  11. </object>

  

以文本方式保存组件属性,比较直观、容易修改,上面 HTML 示例中的 就很清晰。下面开始介绍如何在组件中实现 IPersistPropertyBag 接口。

二、组件的实现

(1)vc6.0 开发步骤

1、建立一个工作空间(WorkSpace)。

2、在这个工作空间中,建立 ATL 工程,示例程序工程为 Simple18。

3、增加 ATL 对象类,默认全部选项。示例程序中的 ATL 对象短名称是 Property。

4、增加一些属性。在以前的章回中,我们只介绍了增加接口函数的方法,由于今天是首次增加接口属性,所以稍微细致一些。步骤是,在ClassView卡片中选择接口(IProperty)后,执行鼠标右键菜单"Add Property..."

5、增加 BSTR 类型的接口属性 str,同样的方式,再增加一个 long 型的接口属性 interger。在示例程序中,这两个属性其实只为演示,并没有实际的意义。

6、接口中的属性,多数情况下会对应对象内部的一个成员变量,因此我们现在要添加成员变量。选择对象类名,执行鼠标右键菜单"Add Member Variable...."

7、添加两个成员变量,一个是 CComBSTR m_str 对应于接口属性 str;另一个是 long m_integer 对应于接口属性 integer。

(2)vc.net 开发步骤

1、建立一个空白解决方案。

2、在解决方案中,新增 ATL 项目。示例程序中项目名称叫 Simple18, 注意不要选择“属性化编程”方式。

3、添加 ATL 类。选择 “ATL 的简单对象”。默认全部选项。示例程序中 ATL 类短名称为 Property,类名称为 CMyProperty。(注1)

4、 增加一些属性。在以前的章回中,我们只介绍了增加接口函数的方法,由于今天是首次增加接口属性,所以稍微细致一些。步骤是,在类视图卡片中选择接口(IProperty)后,执行鼠标右键菜单"添加属性..."

5、增加 BSTR 类型的接口属性 str,同样的方式,再增加一个 long 型的接口属性 interger。在示例程序中,这两个属性其实只为演示,并没有实际的意义。

6、接口中的属性,多数情况下会对应对象内部的一个成员变量,因此我们现在要添加成员变量。选择对象类名,执行鼠标右键菜单"添加变量...."

7、添加两个成员变量,一个是 CComBSTR m_str 对应于接口属性 str;另一个是 long m_integer 对应于接口属性 integer。

(3)实现代码

至此,我们组件的框架已经完成,下面该完成函数函数的实现了:

  1. STDMETHODIMP Cxxx::get_str(BSTR* pVal)
  2. {
  3. *pVal = m_str.Copy();
  4. return S_OK;
  5. }
  6.  
  7. STDMETHODIMP Cxxx::put_str(BSTR newVal)
  8. {
  9. m_str = newVal;
  10. return S_OK;
  11. }
  12.  
  13. STDMETHODIMP Cxxx::get_integer(LONG* pVal)
  14. {
  15. *pVal = m_integer;
  16. return S_OK;
  17. }
  18.  
  19. STDMETHODIMP Cxxx::put_integer(LONG newVal)
  20. {
  21. m_integer = newVal;
  22. return S_OK;
  23. }

  

没有什么复杂的,就是实现 str、integer 两个属性值的设置和读取功能。

(4)添加 IPersistPropertyBag 接口

还记得我们在上回书中如何添加 IPersistStreamInit 的吗?添加 IPersistPropertyBag 的方法也一样,但这次我们换一个方式,即我们不从 IPersistPropertyBag 派生,而是从 IPersistPropertyBagImpl<> 派生。在 ATL 中,系统帮我们已经完成了很多接口的默认实现,我们只要从 IxxxImpl<> 派生,然后再添加一些必要的映射和变量,就可以了。这样显然要比自己去实现接口的所有函数要简单许多了。其实,如果你明白了本回 IPersistPropertyBagImpl<> 派生的方法后,你完全可以修改前回书中的实现方法,从 IPersistStreamInit 派生改进为从 IPersistStreamInitImpl<> 派生。

  1. class ATL_NO_VTABLE Cxxx :
  2. public CComObjectRootEx<...>,
  3. public CComCoClass<...>,
  4. public IDispatchImpl<...>, <b>public IPersistPropertyBagImpl // 手工添加派生类</b> {
  5. ... ... ...
  6.  
  7. BEGIN_COM_MAP(Cxxx)
  8. ... ... ... <b>COM_INTERFACE_ENTRY(IPersistPropertyBag) // 手工添加接口表</b> END_COM_MAP()
  9. ... ... ... <b> // 手工添加属性映射表,这是 IPersistXXXImpl 所必须的。
  10. // 将来你在写 ActiveX 的时候,ATL 向导会帮我们添加属性映射表
  11. BEGIN_PROP_MAP(Cxxx)
  12. // 参数:"属性名称", 接口属性序号(见IDL文件), 属性页对话窗
  13. PROP_ENTRY("str", 1, CLSID_NULL)
  14. PROP_ENTRY("integer", 2, CLSID_NULL)
  15. END_PROP_MAP()</b> ... ... ...
  16. public:
  17. ... ... ... <b>// 这个成员变量,是 IPersistXXXImpl 所必须的
  18. bool m_bRequiresSave; // 表示属性数据是否已经改变而需要保存</b> };

  

我们只要手工添加以上内容,而不用自己写任何 IPersistPropertyBag 接口的函数,多简单呀!天空出彩霞呀,地上开红花呀......会唱这只歌的同学请举手,每个人奖励 vckbase 的专家分 500 !

三、调用者的实现

我们在阅读 MSDN 关于 IPersistPropertyBag 接口函数的时候,你会发现还需要一个接口 IPropertyBag 与之配合才能实现属性包功能。而 IPropertyBag 则需要我们在调用者(容器)中来实现该接口。它们之间的关系如下:

前面几回书中,我们已经学会了从 IUnknown 派生类,也学会了从 IDispatch 派生类,也学会了从 ICallBack 派生类......同样,这回我们要从 IPropertyBag 派生了。在示例程序中,我们添加了一个类 CPropertyBag::public IPropertyBag,同时重载了所有的虚函数。

  1. STDMETHODIMP CPropertyBag::QueryInterface(const struct _GUID &iid,void ** ppv)
  2. {
  3. *ppv = this;
  4. return S_OK;
  5. }
  6.  
  7. ULONG __stdcall CPropertyBag::AddRef(void)
  8. { return 1; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的
  9.  
  10. ULONG __stdcall CPropertyBag::Release(void)
  11. { return 0; } // 做个假的就可以,因为反正这个对象在程序结束前是不会退出的
  12.  
  13. STDMETHODIMP CPropertyBag::Read(LPCOLESTR pszPropName,VARIANT *pVar,IErrorLog *pErrorLog)
  14. {
  15. // 根据 pszPropName 指定的属性名称,你要提供该属性的值。
  16. // 而值的数据类型已经在 pVal->vt 中指定了。
  17. if( 如果能提供指定的数据 ) return S_OK;
  18. else return E_FAIL;
  19. }
  20.  
  21. STDMETHODIMP CPropertyBag::Write(LPCOLESTR pszPropName,VARIANT *pVar)
  22. {
  23. // 根据 psaPropName 指定的属性名称和 pVar 提供的值
  24. // 你保存到文本中去吧。
  25. return S_OK;
  26. }

  

以上是调用者(容器)程序的关键部分,其它的管理和协调部分,读者去阅读示例程序代码。编译注册组件,并运行调用者示例程序,显示如下:

在编辑窗口中你可以随便指定 str 和 interger 的值,然后“启动组件”,那么你设定的属性值就会在启动组件的同时,通过 IPersistPropertyBag 接口设置到组件中(还原了持续性的环境)。而后,你就可以在下面的 Property 分组操作中,“设置/读取”组件的属性了。当“关闭组件”的时候,程序通过调用 IPersistPropertyBag 接口函数,又重新取得组件的属性名称和值保存到编辑窗的文本中了。

四、小结

理解了本回属性包接口的功能,你就能体会出 IE 是如何装载 ActiveX (注2)控件并设置控件的状态了。

注1:在 vc.net 中,由于系统已经有 CProperty 类,所以这里我们改换名称为 CMyProperty。

注2:通过十八回的学习,我们已经了解组件的一些常用接口,为我们学习 ActiveX 的组件编程打下了基础。下回书,我们就开始学习 ActiveX。

【转载】COM 组件设计与应用(十八)——属性包的更多相关文章

  1. 【转载】COM 组件设计与应用(八)——实现多接口

    原文:http://vckbase.com/index.php/wv/1219.html 一.前言 从第五回开始到第七回,咱们用 ATL 写了一个简单的 COM 组件,之所以说简单,是因为在组件中,只 ...

  2. xmlplus 组件设计系列之十 - 网格(DataGrid)

    这一章我们要实现是一个网格组件,该组件除了最基本的数据展示功能外,还提供排序以及数据过滤功能. 数据源 为了测试我们即将编写好网格组件,我们采用如下格式的数据源.此数据源包含两部分的内容,分别是表头数 ...

  3. Kafka设计解析(十八)Kafka与Flink集成

    转载自 huxihx,原文链接 Kafka与Flink集成 Apache Flink是新一代的分布式流式数据处理框架,它统一的处理引擎既可以处理批数据(batch data)也可以处理流式数据(str ...

  4. 菜鸡的Java笔记 第二十八 - java 包的定义

    包的主要作用以及定义    包的导入操作    系统常见的开发包    jar 程序命令        包的定义        在任何的操作系统之中都有一个统一的共识:同一个目录下不能够存在有相同的文 ...

  5. 【转载】COM 组件设计与应用(七)——编译、注册、调用

    原文:http://vckbase.com/index.php/wv/1218.html 一.前言 上两回中,咱们用 ATL 写了第一个 COM 组件程序,这回中,主要介绍编译.册和调用方法.示例程序 ...

  6. 【转载】COM 组件设计与应用(十)——IDispatch 接口 for VC.NET

    原文:http://vckbase.com/index.php/wv/1225.html 一.前言 终于写到了第十回,我也一直期盼着写这回的内容耶,为啥呢?因为自动化(automation)是非常常用 ...

  7. 任务三十八:UI组件之排序表格

    任务三十八:UI组件之排序表格 面向人群: 有一定JavaScript基础 难度: 低 重要说明 百度前端技术学院的课程任务是由百度前端工程师专为对前端不同掌握程度的同学设计.我们尽力保证课程内容的质 ...

  8. Bootstrap入门(十八)组件12:徽章与巨幕

    Bootstrap入门(十八)组件12:徽章与巨幕 1.徽章 2.巨幕 1.徽章 给链接.导航等元素嵌套 <span class="badge"> 元素,可以很醒目的展 ...

  9. 【转载】COM 组件设计与应用(十一)—— IDispatch 及双接口的调用

    原文:http://vckbase.com/index.php/wv/1236.html 一.前言 前段时间,由于工作比较忙,没有能及时地写作.其间收到了很多网友的来信询问和鼓励,在此一并表示感谢.咳 ...

随机推荐

  1. 连接AWS Ubuntu服务器

    1.在AWS上创建了Ubuntu实例后,在实例里点连接.点使用PuTTY连接,下载PuTTY软件. 2.在所有程序里找到PuTTYgen并打开,点Load选择创建实例时的pem文件,点save pri ...

  2. eclipse导入spring aop xml约束

    步骤: 1. 2. 3. 4.取  Location:中最后一个命名:spring-aop-4.2.xsd,放到Key的最后面,Key type:选择 Schema location.点击OK 5.编 ...

  3. [翻译] ValueTrackingSlider

    ValueTrackingSlider What is it? A UISlider Subclass that displays live values in a popUpView. It’s i ...

  4. 深入理解python中的yield关键字

    想必大家都看过这样的代码: 上面的这段代码会计算0-9的平方并打印出来. 那么问题来了,这段代码和我们要说的东西有什么区别呢? 这里的关键字,yield,我在前面的文章里已经发过了.那么yield是什 ...

  5. Lua脚本语法说明(转):

    Lua脚本语法说明(增加lua5.1部份特性) 转自:http://www.cnblogs.com/ly4cn/archive/2006/08/04/467550.html Lua 的语法比较简单,学 ...

  6. PHP设计模式系列 - 建造者模式

    什么是建造者模式 建造者模式主要是为了消除其它对象复杂的创建过程. 设计场景 有一个用户的UserInfo类,创建这个类,需要创建用户的姓名,年龄,金钱等信息,才能获得用户具体的信息结果. 创建一个U ...

  7. 手写HASHMAP

    手写HASHMAP const int MAXN=10010; const int HASH=10100;            //需要hash的数的总个数最大值 struct HASHMAP { ...

  8. iOS AOP框架Aspects实现原理

    总结: Aspects 是对 类的继承结构isa.mataclass结构的调整和维护:相当于链表的节点插入和删除: 同时使用method Swizzling 对方法统一重定向: 同时使用类似代理的机制 ...

  9. BZOJ1444:[JSOI2009]有趣的游戏(AC自动机,矩阵乘法)

    Description Input 注意 是0<=P, n , l, m≤ 10. Output Sample Input input 1 3 2 2 1 2 1 2 AB BA AA inpu ...

  10. 【洛谷】【treap/堆】P2073 送花

    [题目描述:] 这些花都很漂亮,每朵花有一个美丽值W,价格为C. 小明一开始有一个空的花束,他不断地向里面添加花.他有以下几种操作: 操作 含义 1 W C 添加一朵美丽值为W,价格为C的花. 3 小 ...