选择显示字体大小

com组件设计与应用之实现多接口

一、前言

   咱们用 atl 写了一个简单的 com 组件,之所以说简单,是因为在组件中,只实现了一个自定义(custom)的接口 ifun。当然如果想偷懒的话,我们可以把 200 个函数都加到这一个接口中, 果真如此的话,恐怕就没有人喜欢使用我们这个组件了。 一个组件既然可以提供多个接口,那么我们在设计的时候,就应该按照函数的功能进行分类,把不同功能分类的函数用多个接口表现出来。这样可以有如下的一些好处:

1、一个接口中的函数个数有限、功能集中,使用者容易学习、记忆和调用。一个接口到底提供多少个函数合适那?答案是:如果你是黑猩猩,那么一个接口最多3个函数,如果你是人,那么一个接口最好不要超过7个函数。(注1)

2、容易维护。至少你肉眼搜索的时候也方便一些呀。

3、容易升级。当我们给组件增加函数的时候,不要修改已经发表的接口,而是提供一个新的接口来完成功能扩展。(注2)

本回书着落在------如何实现一个组件,多个接口。

  二、接口结构


图一、组件a有2个自定义接口,组件b是a的升级

   某日,我们设计了组件a,它有2个自定义(custom)接口。imathe 有函数add()完成整数加法,istr 有函数cat()完成字符串连接。忽一日,我们升级组件a到b,欲增加一个函数 mul() 完成整数的乘法。注意,由于我们已经发表了组件a,因此我们不能把这个函数安排到老接口 imathe 中了。解决方法是再定义一个新接口 imathe2,在新接口中增加 mul() 函数并依旧保留 add() 函数。这样,老用户不知道新接口 imathe2 的存在,他仍然使用旧接口 imathe;而新用户则可以抛弃 imathe,直接使用 imathe2 的新接口功能。看,多平顺的升级方式呀!

  三、实现

3-1首先用 atl 实现一个自定义(custom)接口 imathe 的com组件,在接口中完成 add()整数加法函数。注意!!!一定是自定义(custom)的接口(dual 双接口以后再介绍)。如果你不了解这个操作,请重新阅读我以前的文章。

3-2查看 idl 文件。完成上一个步骤后,打开idl文件,内容如下:(名称及 uuid 会和你程序中的idl有所不同) 1 import "oaidl.idl";
2 import "ocidl.idl";
3 [
4 object,
5 uuid(072ea6ca-7d08-4e7e-b2b7-b2fb0b875595),
6 helpstring("imathe interface"),
7 pointer_default(unique)
8 ]
9 interface imathe : iunknown
10 {
11 [helpstring("method add")] hresult add([in] long n1, [in] long n2, [out,retval] long *pnval);
12 };
13 [
14 uuid(cd7672f7-c0b4-4090-a2f8-234c0062f42c),
15 version(1.0),
16 helpstring("simple3 1.0 type library")
17 ]
18 library simple3lib
19 {
20 importlib("stdole32.tlb");
21 importlib("stdole2.tlb");
22 [
23 uuid(c6f241e2-43f6-4449-a024-b7340553221e),
24 helpstring("mathe class")
25 ]
26 coclass mathe
27 {
28 [default] interface imathe;
29 };
30 };
1-2引入 iunknown 和atl已经定义的其它接口描述文件。import 类似与 c 语言中的 #include
3-12一个接口的完整描述
4object 表示本块描述的是一个接口。idl文件是借用了prc远程数据交换格式的说明方法
5uuid(......) 接口的 iid,这个值是 atl 自动生成的,可以手工修改或用 guidgen.exe 产生(注3)
6在某些软件或工具中,能看到这个提示
7定义接口函数中参数所使用指针的默认属性(注4)
9接口叫 imathe 派生自 iunknown,于是 imathe 接口的头三个函数一定就是queryinterface,addref和release
10-12接口函数列表
13-30类型库的完整描述(类型库的概念以后再说),下面所说明的行,是需要先了解的
18#import 时候的默认命名空间
23组件的 clsid,cocreateinstance()的第一个参数就是它
27-29接口列表
28[default]表示谁提供了iunknown接口

  3-3、手工修改idl文件,黑体字部分是手工输入的。完成后保存。import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(072ea6ca-7d08-4e7e-b2b7-b2fb0b875595),
helpstring("imathe interface"),
pointer_default(unique)
]
interface imathe : iunknown
{
[helpstring("method add")] hresult add([in] long n1, [in] long n2, [out,retval] long *pnval);
};
[ // 所谓手工输入,其实也是有技巧的:把上面的接口描述(imathe)复制、粘贴下来,然后再改更方便哈
object, uuid(072ea6cb-7d08-4e7e-b2b7-b2fb0b875595),
// 手工或用工具产生的 iid
helpstring("istr interface"),
pointer_default(unique)
]
interface istr : iunknown
{
// 目前还没有任何接口函数 }; [ uuid(cd7672f7-c0b4-4090-a2f8-234c0062f42c), version(1.0), helpstring("simple3 1.0 type library")
]
library simple3lib
{
importlib("stdole32.tlb"); importlib("stdole2.tlb");
[
uuid(c6f241e2-43f6-4449-a024-b7340553221e),
helpstring("mathe class")
]
coclass mathe
{
[default] interface imathe;
interface istr; // 别忘了呦,这里还有一个那
};
};

3-4、打开头文件(mathe.h),手工增加类的派生关系和接口入口表 ,然后保存。class atl_no_vtable cmathe :
public ccomobjectrootex <ccomsinglethreadmodel>,
public ccomcoclass <cmathe, &clsid_mathe>,
public imathe, // 别忘了,这里加一个逗号
public istr // 增加一个基类
{
public:
cmathe()
{
}
declare_registry_resourceid(idr_mathe)
declare_protect_final_construct()
begin_com_map(cmathe) // 接口入口表。这里填写的接口,才能被queryinterface()找到
com_interface_entry(imathe)
com_interface_entry(istr)
end_com_map()

  3-5、好了,一切就绪。接下来,就可以在 istr 接口中增加函数了。示例程序中增加一个字符串连接功能的函数:

hresult cat([in] bstr s1, [in] bstr s2, [out,retval] bstr *psval); 如果你不知道如何做,请重新阅读前三回的内容。
  
  四、接口升级

  我们这个组件已经发行了,但忽然一天我们需要在 imathe 接口上再增加一个函数......不行!
绝对不能在 imathe 上直接修改!怎么办?解决方法是------再增加一个接口,我们就叫 imathe2 吧,
如果以后还要增加函数,那么我们再增加一个接口叫 imathe3......子子孙孙,无穷尽也。

4-1、修改 idl 文件,其实如果你理解了上面一小节的内容,再增加一个接口是很简单的事情了。import "oaidl.idl";
import "ocidl.idl";
[
object,
uuid(072ea6ca-7d08-4e7e-b2b7-b2fb0b875595),
helpstring("imathe interface"),
pointer_default(unique)
]
interface imathe : iunknown
{
[helpstring("method add")] hresult add([in] long n1, [in] long n2, [out,retval] long *pnval);
};
[ object,
uuid(072ea6cb-7d08-4e7e-b2b7-b2fb0b875595),
helpstring("istr interface"),
pointer_default(unique)
]
interface istr : iunknown
{
[helpstring("method cat")] hresult cat([in] bstr s1, [in] bstr s2, [out,retval] bstr *psval);
};
[
object,
uuid(072ea6cc-7d08-4e7e-b2b7-b2fb0b875595),
helpstring("imathe2 interface"),
pointer_default(unique)
]
interface imathe2 : iunknown
{
// 下面这个add()函数,只有idl中的声明,而不用增加任何程序代码,因为这个函数早在 imathe 中就已经实现了
[helpstring("method add")] hresult add([in] long n1, [in] long n2, [out,retval] long *pnval);
};

[
uuid(cd7672f7-c0b4-4090-a2f8-234c0062f42c),
version(1.0),
helpstring("simple3 1.0 type library")
]
library simple3lib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(c6f241e2-43f6-4449-a024-b7340553221e),
helpstring("mathe class")
]
coclass mathe
{
[default] interface imathe;
interface istr;
interface imathe2; // 别忘了,这里还有一行呢!
};
};
4-2、打开头文件,增加派生关系和接口入口表class atl_no_vtable cmathe :
public ccomobjectrootex <ccomsinglethreadmodel>,
public ccomcoclass <cmathe, &clsid_mathe>,
public imathe,
public istr, // 这里增加一个逗号
public imathe2 {
public:
cmathe()
{
}
declare_registry_resourceid(idr_mathe)
declare_protect_final_construct() begin_com_map(cmathe)
com_interface_entry(imathe)
com_interface_entry(istr)
com_interface_entry(imathe2)
end_com_map()
  
4-3、示例程序中,增加了一个整数乘法函数:

hresult mul([in] long n1, [in] long n2, [out,retval] long *pnval);
如果你不知道如何做,那就别学了:-( 都讲好几遍了,怎么还不掌握呢?知道狗熊是怎么死的吗?(注5)

  4-4、因为我们的组件升级了,于是也应该修改版本号了(这不是必须的,但最好修改一下)。
打开注册表文件(.rgs) 把有关progid的版本 "mathe.1" 修改为"mathe.2"。
另外如果你愿意,把idl文件中的 version 和提示文字一并修改一下。这里就不再粘贴文件内容了,
因为很简单,大家下载示例程序(注6)后,自己看吧。


 


关键字 本文所属关键字

相关 与本文相关文章

分类 所有文章关键字导航

源码编程相关

Java   Asp   PHP   .Net   XML   C/C++   CGI   VB   Jsp   J2ee   J2se   J2me   EJB   Servlet   Tomcat   Resin   Struts   Weblogic   Eclipse   ANT   GUI   JMS   Web servise   IDEA   Webphere   Hibernate   Spring   Jboss   Applet   Swing   Socket   Javamail   Perl   Ajax   P2P   安全   模式   框架   测试   开源   游戏

SQL数据库相关

My-SQL   Ms-SQL   Access   DB2   Oracle   Sybase   SQLserver   索引   存储过程   加密   数据库   分页   视图  

手机无线相关

3G   Wap   CDMA   GRPS   GSM   IVR   彩信   短信   无线   增值业务

网页设计制作相关

HTML   CSS   网页配色   网页特效   Javascript   VBscript   Dreamweaver   Frontpage   JS   Web   网站设计

网站建设推广相关

建站经验   网站优化   网站排名   推广   Alexa

操作系统/服务器相关

Windows XP   Windows 2000   Windows 2003   Windows Me   Windows 9.x   Linux   UNIX   注册表   操作系统   服务器   应用服务器

图形图像多媒体相关

Photoshop   Fireworks   Flash   Coreldraw   Illustrator   Freehand   Photoimpact   多媒体   图形图像

标准 网站致力的规范

Valid CSS!

无不良内容,无不良广告,无恶意代码

Valid XHTML 1.0 Transitional

creativecommons