博文
COM组件设计与应用(八)(2006-02-21 20:13:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1501
实现多接口
作者:杨老师
下载源代码
一、前言
从第五回开始到第七回,咱们用 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 的新接口功能。看,多平顺的升级方式呀!
三、实现
&n......
COM 组件设计与应用(七)(2006-02-20 12:28:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1500
编译、注册、调用
作者:杨老师
一、前言
上两回中,咱们用 ATL 写了第一个 COM 组件程序,这回中,主要介绍编译、注册和调用方法。示例程序你已经下载了吗?如果还没有下载,vc6.0 的用户点这里,vc.net 的用户点这里。
二、关于编译
2-1 最小依赖
“最小依赖”,表示编译器会把 ATL 中必须使用的一些函数静态连接到目标程序中。这样目标文件尺寸会稍大,但独立性更强,安装方便;反之系统执行的时候需要有 ATL.DLL 文件的支持。如何选择设置为“最小依赖”呢?答案是:删除预定义宏“_ATL_DLL”,操作方法见图一、图二。
图一、在vc6.0中,设置方法
图二、在 vc.net 2003中,设置方法
2-2 CRT库
如果在 ATL 组件程序中调用了 CRT 的运行时刻库函数,比如开平方 sqrt() ,那么编译的时候可能会报错“error LNK2001: unresolved external symbol _main”。怎么办?删除预定义宏“_ATL_MIN_CRT”!操作方法也见图一、图二。(vc.net 2003 中的这个项目属性叫“在 ATL 中最小使用 CRT”)
2-3 MBCS/UNICODE
这个不多说了,在预定义宏中,分别使用 _MBCS 或 _UNICODE。
2-4 IDL 的编译
COM 在设计初期,就定了一个目标:要能实现跨语言的调用。既然是跨语言的,那么组件的接口描述就必须在任何语言环境中都要能够认识。怎么办?用 .h 文件描述?------ C语言程序员笑了,真方便!BASIC 程序员哭了:-( 因此,微软使用了一个新的文件格式---IDL文件(接口定义描述语言)。IDL 是一个文本文件,它的语言语法比较简单,很象C。具体 IDL 文件的讲解,见下一回《COM 组件设计与应用(八)之添加新接口》。IDL 经过编译,生成二进制的等价类型库文件 TLB 提供给其它语言来使用。图三示意了 ATL COM 程序编......
COM 组件设计与应用(六)(2006-02-20 12:27:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1498
用 ATL 写第一个组件
作者:杨老师
下载源代码
一、前言
1、与 《COM 组件设计与应用(五)》的内容基本一致。但本回讲解的是在 vc.net 2003 下的使用方法,即使你不再使用vc6.0,也请和上一回的内容,参照比对。
2、这第一个组件,除了所有 COM 组件必须的 IUnknown 接口外,我们再实现一个自己定义的接口 IFun,它有两个函数: Add()完成两个数值的加法,Cat()完成两个字符串的连接。
3、下面......好好听讲! 开始了:-)
二、建立 ATL 工程
步骤2.1:建立一个解决方案。
步骤2.2:在 该解决方案中,新建一个 vc++ 的 ATL 项目。示例程序叫 Simple2,并选择DLL方式,见图一、图二。
图一、新建 ATL 项目
图二、选择非属性化的DLL组件类型
属性化 属性化编程,是未来的方向,但我们现在先不要选它。
动态链接库(DLL) 选择它。
可执行文件(EXE) 以后再讲。
服务(EXE) 表示建立一个系统服务组件程序,系统启动后就会加载并执行的程序。
允许合并代理/存根(stub)代码 选择该项表示把“代理/存根”代码合并到组件程序中,否则需要单独编译,单独注册代理存根程序。代理/存根,这个是什么概念?还记得我们在上回书中介绍的吗?当调用者调用进程外或远程组件功能的时候,其实是代理/存根负责数据交换的。关于代理/存根的具体变成和操作,以后再说啦......
支持 MFC 除非有特殊的原因,我们写 ATL 程序,最好不要选择该项。你可能会说,如果没有MFC的支持,那CString怎么办呀?告诉你个秘密吧,一般人我都不告诉他,我后半辈子就靠着这个秘密活着了:
1、你会STL吗?可以用 STL 中的 string 代替;
2、自己写个 MyString 类,嘿嘿;
3、悄悄地、秘密地、不要告诉别人(特别是别告诉微软),把 MFC 中的 CString 源码拿......
COM 组件设计与应用(五)(2006-02-20 12:26:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1497
用 ATL 写第一个组件
作者:杨老师
下载源代码
一、前言
1、如果你在使用 vc5.0 及以前的版本,请你升级为 vc6.0 或 vc.net 2003;
2、如果你在使用 vc6.0 (ATL 3.0)请阅读本回内容;
3、如果你在使用 vc.net(ATL 7.0)请阅读下回内容;(当然读读本文内容也不错)
4、这第一个组件,除了所有 COM 组件必须的 IUnknown 接口外,我们再实现一个自己定义的接口 IFun,它有两个函数: Add()完成两个数值的加法,Cat()完成两个字符串的连接。
5、下面......好好听讲! 开始了:-)
二、建立 ATL 工程
步骤2.1:建立一个工作区(WorkSpace)。
步骤2.2:在工作区中,建立一个 ATL 工程(Project)。示例程序叫 Simple1,并选择DLL方式,见图一。
图一、建立 ATL DLL 工程
Dynamic Link Library(DLL) 表示建立一个 DLL 的组件程序。
Executable(EXE) 表示建立一个 EXE 的组件程序。
Service(EXE) 表示建立一个服务程序,系统启动后就会加载并执行的程序。
Allow merging of proxy/stub code 选择该项表示把“代理/存根”代码合并到组件程序中,否则需要单独编译,单独注册代理存根程序。代理/存根,这个是什么概念?还记得我们在上回书中介绍的吗?当调用者调用进程外或远程组件功能的时候,其实是代理/存根负责数据交换的。关于代理/存根的具体变成和操作,以后再说啦......
Support MFC 除非有特殊的原因,我们写 ATL 程序,最好不要选择该项。你可能会说,如果没有MFC的支持,那CString怎么办呀?告诉你个秘密吧,一般人我都不告诉他,我后半辈子就靠着这个秘密活着了:
1、你会STL吗?可以用 STL 中的 string 代替;
2、自己写个 MyStri......
COM组件设计与应用(四)(2006-02-19 12:03:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1493
简单调用组件
作者:杨老师
一、前言
同志们、朋友们、各位领导,大家好。
VCKBASE 不得了,
网友众多文章好。
组件设计怎么学?
知识库里闷头找!
摘自---杨老师打油集录
在 VCKBASE 的顶力支持下,在各位网友回帖的鼓励下,我才能顺利完成系列论文的前三回。书到本回,我们终于开始写代码啦。写点啥那?恩,有了!咱们先从如何调用现成的简单的组件开始吧,同时也顺便介绍一些相关的知识。
二、组件的启动和释放
在第三回中,大家用“小本本”记录了一个原则:COM 组件是运行在分布式环境中的 。于是,如何启动组件立刻就遇到了严重的问题,大家看这段代码: p = new 对象;
p->对象函数();
delete p;
这样的代码再熟悉不过了,在本地进程中运行是不会有问题的。但是你想想,如果这个对象是在“地球另一边”的计算机上,结果会如何?嘿嘿,C++ 在设计 new 的时候,可没有考虑远程的实现呀(计算机语言当然不会,也没必要去设计)。因此启动组件、调用接口的功能,当然就由 COM 系统来实现了。
图一 组件调用机制
由上图可以看出,当调用组件的时候,其实是依靠代理(运行在本地)和存根(运行在远端)之间的通讯完成的。具体来说,当客户程序通过 CoCreateInstance() 函数启动组件,则代理接管该调用,它和存根通讯,存根则它所在的本地(相对于客户程序来说就是远程了)执行 new 操作加载对象。对于初学者,你可以不用理它,代理和存根对我们来说是透明的。只要大约知道是怎么一回事就一切OK了。
问题又来了,这个远程的对象什么时候消灭呢?在第二回介绍接口概念的时候,当时我们特意忽略了两个函数,就是IUnknown::AddRef()和IUnknown::Release(),从函数名就能猜到了,一个是对内部引用记数器(Ref)加1,一个是释放(减1),当记数器减为0的时候,就是......
COM组件设计与应用(三)(2006-02-19 12:02:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1488
数据类型
作者:杨老师
一、前言
上回书介绍了GUID、CLSID、IID和接口的概念。本回的重点是介绍 COM 中的数据类型。咋还不介绍组件程序的设计步骤呀?咳......别着急,别着急!孔子曰:“饭要一口一口地吃”;老子语:“心急吃不了热豆腐”,孙子云:“走一步看一步吧” ...... 先掌握必要的知识,将来写起程序来才会得心应手也:-)
走入正题之前,请大家牢牢记住一条原则:COM 组件是运行在分布式环境中的。比如,你写了一个组件程序(DLL或EXE),那么使用者可能是在本机的某个进程内加载组件(INPROC_SERVER);也可能是从另一个进程中调用组件的进程(LOCAL_SERVER);也可能是在这台计算机上调用地球那边计算机上的组件(REMOTE_SERVER)。所以在理解和设计的时候,要时时刻刻想起这句话。快!拿出小本本,记下来!
二、HRESULT 函数返回值
每个人在做程序设计的时候,都有他们各自的哲学思想。拿函数返回值来说,就有好多种形式。
函数
返回值
返回值信息
double sin(double)
浮点数值
计算正玄值
BOOL DeleteFile(LPCTSTR)
布尔值
文件删除是否成功。如失败,需要GetLastError()才能取得失败原因
void * malloc(size_t)
内存指针
内存申请,如果失败,返回空指针 NULL
LONG RegDeleteKey(HKEY,LPCTSTR)
整数
删除注册表项。0表示成功,非0失败,同时这个值就反映了失败的原因
UINT DragQueryFile(HDROP,UINT,LPTSTR,UINT)
整数
取得拖放文件信息。以不同的参数调用,则返回不同的含义:
一会儿表示文件个数,一会儿表示文件名长度,一会儿表示字符长度
...... ......
...
...... ......
如此纷繁复杂的返回值,如此含义多变的返回值,使得大家在学习和使用的过程中,增加了......
COM组件设计与应用(二)(2006-02-19 12:01:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1485
GUID 和 接口
作者:杨老师
一、前言
书接上回,话说在 doc(Word) 复合文件中,已经解决了保存 xls(Excel) 数据的问题了。那么,接下来又要解决另一个问题:当 WORD 程序读取复合文件,遇到了 xls 数据的时候,它该如何启动 Excel 呢?启动后,又如何让 Excel 自己去读入、解析、显示 xls 数据呢?
二、CLSID 概念
有一个非常简单的解决方案,那就是在对象数据的前面,保存有处理这个数据的程序名。(见下图左上)
图一、CLSID 的概念
这的确是一个简单的方法,但同时问题也很严重。在“张三”的计算机上,Excel 的路径是:"c:\office\Excel.exe",如果把这个 doc 文件复制到“李四”的计算机上使用,而“李四”的 Excel 的路径是:
"d:\Program files\Microsoft Office\Office\Excel.exe",完蛋了:-(
于是,微软想出了一个解决方案,那就是不使用直接的路径表示方法,而使用一个叫 CLSID(注1)的方式间接描述这些对象数据的处理程序路径。CLSID 其实就是一个号码,或者说是一个16字节的数。观察注册表(上图),在HKCR\CLSID\{......}主键下,LocalServer32(DLL组件使用InprocServer32) 中保存着程序路径名称。CLSID 的结构定义如下:typedef struct _GUID {
DWORD Data1; // 随机数
WORD Data2; // 和时间相关
WORD Data3; // 和时间相关
BYTE Data4[8]; // 和网卡MAC相关
} GUID;
typedef GUID CLSID; // 组件ID
typedef GUID IID; // 接口ID
#define REFCLSID const CLSID &
// 常见的声明和赋值方法
CLSID CLSID_Excel = {0x00024500,0x0000......
COM 组件设计与应用(一)(2006-02-18 18:46:00)
摘要:原文出处:http://www.vckbase.com/document/viewdoc/?id=1483
起源及复合文件
作者:杨老师
一、前言
公元一九九五年某个夜黑风高的晚上,我的一位老师跟我说:“小杨呀,以后写程序就和搭积木一样啦。你赶快学习一些OLE的技术吧......”,当时我心里就寻思 :“开什么玩笑?搭积木方式写程序?再过100年吧......”,但作为一名听话的好学生,我开始在书店里“踅摸”(注1)有关OLE的书籍(注2)。功夫不负有心人,终于买到了我的第一本COM书《OLE2 高级编程技术》,这本800多页的大布头花费了我1/5的月工资呀......于是开始日夜耕读.....
功夫不负有心人,我坚持读完了全部著作,感想是:这本书,在说什么呐?
功夫不负有心人,我又读完了一遍大布头,感想是:咳~~~,没懂!
功夫不负有心人,我再,我再,我再读 ... 感想是:哦~~~,读懂了一点点啦,哈哈哈。
...... ......
功夫不负有心人,我终于,我终于懂了。
800页的书对现在的我来说,其实也就10几页有用。到这时候才体会出什么叫“书越读越薄”的道理了。到后来,能买到的书也多了,上网也更方便更便宜了......
为了让VCKBASE上的朋友,不再经历我曾经的痛苦、不再重蹈我“无头苍蝇”般探索的艰辛、为了VCKBASE的蓬勃发展、为了中国软件事业的腾飞(糟糕,吹的太也高了)......我打算节约一些在 BBS 上赚分的时间,写个系列论文,就叫“COM组件设计与应用”吧。今天是第一部分——起源。
二、文件的存储
传说350年前,牛顿被苹果砸到了头,于是发现了万有引力。但到了二十一世纪的现在,任何一个技术的发明和发展,已经不再依靠圣人灵光的一闪。技术的进步转而是被社会的需求、商业的利益、竞争的压力、行业的渗透等推动的。微软在Windows平台上的组件技术也不例外,它的发明,有其必然因素。什么是这个因素那?答案是——文件的存储。
打开记事本程序,输入了一篇文章后,保存。——这样的文件叫“非结构化文件”;
打开电子表格程序,输入一个班的学生姓名和考试成绩,保存。——这样的文件叫“标准结构化文件”;
在我们写的......