Programming 版 (精华区)
发信人: lofe ()感激生活(), 信区: Programming
标 题: 组件、COM和ATL(1)
发信站: 哈工大紫丁香 (Sun Sep 3 08:03:53 2000), 转信
发信人: kywu (太阳星辰), 信区: VisualC
标 题: 组件、COM 和 ATL(1)
发信站: BBS 水木清华站 (Mon Mar 22 13:05:12 1999) WWW-POST
GUI 医生点评:组件、COM 和 ATL
GUI 医生 Online
第一部分:1998 年 2 月 2 日
第二部分:1998 年 2 月 9 日
第三部分:1998 年 2 月 23 日
第四部分:1998 年 3 月 2 日
编辑评注 在 Visual Studio 6.0 发布时,只能提供本栏目的第一到第四部分。在 1998
年 7 月发布 MSDN Library 时,本栏目的全部内容将再版。您也可以参阅 MSDN Online
(http://www.microsoft.com/msdn/) 上每周的“GUI 医生 Online”,并且查看本栏目和
以前栏目的归档部分。
目录
第一部分:您打算解释 COM 吗?以前不是已经有人解释过了吗?
第二部分:COM 基本概念
第三部分:获取对象和接口
第四部分:对象类和对象库
第一部分:您打算解释 COM 吗?以前不是已经有人解释过了吗?
医生听到您说:“您要干什么?解释 COM?已经有人写了这方面的书!”
确实有书了,而且 GUI 医生还可以推荐很多好书。他很喜欢 Dale Rogerson 的 Inside
COM (Microsoft Press, 1997) 和 Don Box 的 Essential COM (Addison-Wesley, 1998)
。Adam Denning 的 ActiveX Controls Inside Out (Microsoft Press, 1996),Sing
Li 和 Panos Economopoulos 的 Professional Visual C++ 5 ActiveX/COM Control
Programming (WROX Press, 1997) 也是好书。当然,您也应该看 Kraig Brockschmidt
的 Inside OLE, 2nd Edition (Microsoft Press, 1995),这是 OLE 参考的最好印刷品
。(当然,MSDN 也是最好的参考,不只是医生这样认为。)另外,如果您想要看客观地
解释组件软件的书,请参考 Clemens Szyperski 的新书 Component Software: Beyond
Object-Oriented Programming (Addison-Wesley, 1998)。
“那又怎么样,”您问到,“GUI 医生还能比这些书说得更好吗?”
“根本不会。”医生这样回答。这些作者(和其他作者)再加上文档确实已经全说到了。
如果您有时间看这些书,就是这样。
“那么您要做的就是,”您叫道,“为我们轻描淡写地介绍一番 COM 和 ATL,而且每周
只有一小段!”
GUI 医生回敬道:“您很聪明。这样就比我预料的要容易多了。”
组件?我是在编写一个立体声系统吗?
如果您很熟悉 Visual Basic,您就会很熟悉使用组件编写程序:您既使用 visual
ActiveX 控件(例如微调按钮),也使用非可视的 ActiveX 组件(例如数据库访问对象
)。很难找到一个没有频繁使用预制的可重用组件的重要 Visual Basic 程序。但是,虽
然您重用了大量的组件,大多数人并没有为自己编写过完整的可重用组件。
如果您使用 C++ 进行编程,那么您对重用会有不同的感受。人们说 C++ 和面向对象编程
可以很容易地实现对象重用,但是您的经验怎么样?您编写过可重用对象库吗?无疑少数
人确实编写过,但是大部分人并非如此。并且,就算有了这样的一个库,我们能很好地利
用它吗?并不只是由于缺乏规则从而使我们难以重用代码;事实是,代码很难重用(代码
好象从不按我们需要的方式执行),而编写可重用代码则更难(很难既足够通用又足够实
用)。
结果是,C++ 并没有使创建可重用二进制组件变得容易,而是使重用源代码变得相对容易
。要知道,大多数主要的 C++ 库是以源代码形式发布的,而不是编译后的形式。为了正
确地继承一个对象,很有必要查看这些源代码,并且依赖于原库的实现细节来进行重用是
很容易的(也是有必要的)。更糟的是,修改源代码并自己连编原库是很诱人的(或是必
要的)。(到底有多少自连编的 MFC?谁也不知道 . . . .)
那么让我们重用二进制对象,而不是源代码
那么又怎能重用二进制对象呢?Windows 程序员首先想到的答案很简单:使用动态链接库
(DLL)。使用 DLL 确实管用,毕竟 Windows 本身就主要是一组 DLL。但是,还有一些问
题。
首先,DLL 不是独立于编程语言的。即使是使用 C 语言编写的 DLL,也很容易更改调用
约定(按什么顺序将什么参数入栈),从而使该 DLL 只能用在 C 程序中。就算
Windows 使用的调用约定已作为 Windows 系统的标准规定得很完备,但是,医生仍遇到
过由于调用约定不匹配而导致的 DLL 失败。
为 DLL 编写一个 C 语言的接口有一些重要限制。首先,这限制了您进行面向对象编程,
因为,C++ 的面向对象特性需要对函数名进行修饰,名称修饰没有统一的标准。有些情况
下,甚至同一编译器的不同版本对名称的修饰也会不同。第二,实现多态性将很困难。通
过创建包装类,您可以解决这些问题,但是这样做是痛苦的。GUI 医生没有痛苦过。(起
码没有太痛苦过。)
即使您解决了名称修饰问题,并进而成功地链接到了 DLL,当更新对象时也会出现其他问
题。
首先,如果当您更新对象时要向其中添加任何虚函数,那么您就会像浑身插满软管的病人
一样动弹不得。您可能会认为在对象的末尾添加新函数不会出问题,但是实际上并非如此
:这样会将所有从您的对象派生的对象的虚函数表入口平移。并且,因为调用虚函数需要
使用虚函数表中的固定偏移,以便调用正确的函数,所以您不能对虚函数表进行任何更改
——至少不重新编译每个有关的程序(这些程序使用您的对象或任何从您的对象派生的对
象),就不能进行更改。很明显,每次更新您的对象时都重新编译全世界,不是个好主意
。
其次,如果您在客户程序中使用 new 来分配对象,那么您要更改该对象的大小(即添加
任何数据),就必须重新编译全世界。
最后(也是最重要的),更新 DLL 简直就是一场恶梦,因为您处于两难境地,两种选择
都很令人倒胃口:要么通过覆盖 DLL 来“就地”更新该 DLL,要么重新命名一个新的版
本。就地更新 DLL 很糟糕:即使您保持接口的统一,DLL 的某些用户程序也会被破坏,
这样的几率很高。GUI 医生就不必一一告诉您业界(包括 Microsoft 在内)因此问题而
遇到的所有麻烦了。
另一种方法,即使用一个新的 DLL 名称,至少能让原来正常运转的系统继续正常运转。
但是,代价是需要占用硬盘空间(也许当普通的硬盘有 3GB 左右大小时,这不是个大问
题),而第二个代价是:增加了内存的使用。如果用户使用了两种版本的 DLL,那么在用
户的工作集内就会存在两个代码极其相似的复本。例如,通常,当您检查用户内存的使用
情况时,就会发现两三个版本的 Visual Basic 运行时模块或 Microsoft Foundation
Class (MFC) DLL。既然几乎所有的 Windows 系统通常都使用比物理内存更多的虚拟内存
,增加工作集的大小就意味着严重的性能问题,表现在增加了交换到硬盘上的虚拟内存的
大小。(这就为 Brook 定律提供了反例:向一个慢的系统添加更多的内存反而会使它更
快。)
在理想情况下,您希望能让用户(或应用程序)来选择使用哪个版本。这对于静态链接的
DLL 是极其困难的,但是对动态加载的 DLL 就很容易了。
公平地讲,应该指出,C++ 从来也没打算解决这类问题。C++ 的原本用途是在只有一个文
件的程序中重用代码,这样所有的对象是同时编译的。C++ 并不打算提供一种建立可重用
二进制组件的方法,以便可以混合使用不同版本和时间的组件。顺便说一句,Java 的缔
造者注意到了这些问题,这些不足是开发 Oak 的主要原因,Oak 后来变成了 Java。
Java 的情况又如何?
Java 确实解决了这些问题中的一部分,但是它也引入了一些自己的问题。最大的问题是
Java 组件(通常是 JavaBeans)只能用于使用 Java 编写的程序。现在,Microsoft 虚
拟机 (VM) 确实允许您将 JavaBeans 用作 COM 对象,从而可以在任何语言中使用它们。
而且,Sun 确实有一个 Java/ActiveX bridge。但是,总的说来,除非在 Windows 中运
行程序,否则 Java 仍是一个单语言的系统:Java 组件只能用于 Java 程序。并且,大
多数情况下,为了使用 Java,您必须从头开始重新编写系统。(是的,您可以进行本地
调用,但是,使用 Java Native Interface(JNI) 非常麻烦,而且程序将再也无法移植了
。)GUI 医生认为这很不可取,所以他很高兴 Microsoft 虚拟机 (VM) 更为灵活,至少
对 Windows 而言是这样。没有哪种语言,甚至包括 C++、Visual Basic 或 Java,能适
合于每位程序员和解决每个问题。
当您用 Java 编写程序时,还必须确定要使用的组件是本地的(在您的计算机上)还是远
程的(在另一台计算机上),而且使用本地和远程组件的方法很不相同。
Java 还有一些其他问题,使它还不能成为满足所有组件需要的理想工具。首先,它还没
有真正可靠的方法解决版本问题。(Microsoft VM 中的打包管理程序对此会有很大帮助
。)其次,Java 多少要比 C++ 慢一些。GUI 医生注意到,在一种 Java 联机杂志上出版
的“象 C++ 一样快”的性能测试中,遗漏了 Java 会表现不好的测试。能想到的两个例
子是字符串和数组操作(Java 必须在每次访问时进行越界检查),以及初始的方法程序调
用(在第一次调用时,Java 必须在类中的一个表内按签名查找该方法程序。当然在后续
的调用会很快,那个 Java 杂志所测试的正是后续的调用。)。最后,Java 的“一次一
个类”的加载机制会比一次加载所有的代码慢得多(即使代码很少!),因为它需要更多
的文件或 HTTP 事务,这些都需要极高的开销。
即使您是按能够获得良好性能的方法使用 Java,当您从另一种语言中使用 Java 组件时
,性能也会很糟,因为需要存在翻译层,以连接不相似的语言和对象模型。
Java 的闪光之处在于存在这样一种可能,即您可以在不同的计算机上使用编译好的组件
,而不必为每种计算机的处理器和操作系统重新编译。但是,这经常并不是那么回事,在
要支持的每种平台上,都需要测试和调试您的组件。
那么,还有别的选择吗?
正如所证实的,可以使用 C++ 连编 DLL 和其他可重用的二进制组件。在 Dale
Rogerson 的书 Inside COM 和 Don Box 的书 Essential COM 中,他们都以一个要重用
的 C++ 类开始,然后使用一些聪明的技巧解决了我上面列出的每个问题(还有其他一些
问题)。毫不奇怪地,他们最后都得出了同样的结果,即 COM。也就是说,二进制代码重
用的每个问题的解决方法都是 COM 的一个重要特性。(如果您想要现在就查看这个过程
,请查阅 Markus Horstmann 的文章“从 CPP 到 COM”。)
虽然 COM 的“母语”是 C++,从 C 程序中也可以很方便地使用 COM ——甚至头文件就
支持这样做。而且,通过一些技巧,在任意一种语言中都可能实现双向 COM 支持,例如
Visual Basic、Java、Delphi 等等。(“双向 COM 支持”的意思是指,有可能既在一种
语言中使用 COM 对象,又使用这种语言编写 COM 对象。)在语言的运行时模块中实现
COM 兼容性的工作并不简单,但是好处是巨大的:一旦这样做了,您就拥有了大量已经编
写和调试好的 COM 对象,以供您使用。而且,COM 组件会有广阔的市场——Giga
Information Group 估计当前的市场是每年 4 亿美元,在三年后预计为 30亿美元。(
COM 组件市场比 Microsoft 的增长还要快!)要注意,这些市场预测是针对第三方 COM
对象的,不包括由 Microsoft 提供的 COM 组件。
COM 的另一个关键特性是支持三种类型的对象:进程内 (DLL)、本地(同一计算机上不同
进程中的 EXE)以及远程(不同计算机中的 DLL 或 EXE,通过分布式 COM 或称 DCOM 来
通讯)。您在编写使用 COM 组件的代码时,不必考虑(甚至知道)最后要使用哪种 COM
对象,因此可使用完全相同的代码来连接进程内、本地或远程对象。COM 是怎样连接到正
确的对象上的呢?是这样,它在注册表中查找对象的 Class ID——注册表项告诉 COM 哪
种对象可用。COM 做其余的工作,包括启动进程和通过网络通讯。(注意:不同种类的
COM 对象存在性能差异,对此您需要心中有数——但是,不管您最后使用哪种对象,至少
用于连接和使用对象的代码是完全相同的。)
但是,COM 并不能解决世界上的所有问题。例如,当您更新一个组件时,仍有可能破坏使
用该组件的程序。(可能由于 COM 强制地为组件赋予“黑盒子”视图,从而不可能了解
组件的实现细节,也就使得这种破坏并不普遍,但是仍会发生。)所以,您仍需要选择是
就地更新组件而承担破坏的风险,还是为新组件使用新的 Class ID。但是,有了 COM,
确实可以较为容易地编写一些代码,以使用户(或应用程序)能够选择使用哪种版本的组
件,而不必重新编译。
回忆一下,几乎可以使用任何语言来编写和使用 COM 组件,而且它们可以存放在任何计
算机上。这很好。但是,跨平台支持又怎样呢?
跨平台的情况是喜忧参半的。忧的是,现在除了 Win32,还没有太多其他平台上的 COM。
有一些移植到非 Windows 平台的 COM,但是不多。不过,这只是忧的一面。
好消息是,很快会进行很多的移植,包括对最常见的 UNIX 版本和对 MVS 的移植。而且
,Microsoft 在亲自进行一些移植工作。COM 和 DCOM 在您最喜欢的主机和 UNIX 计算机
中可用的日子不会太远了,按计划,用于 UNIX 的 COM 在二月份发布。想想看,在一些
快速的主机上运行使用任意的语言编写的远程 COM 对象,而您可以通过您的计算机上的
任何语言(Visual Basic、Java、Delphi、C++)访问该主机,这是多么酷啊!请查阅
Microsoft COM Web 站点 (http://www.microsoft.com/com/)上的最新信息。
所以,如果您正在为 Windows 编程,您一定会考虑编写 COM 对象,而不管您是使用
Visual Basic、Java、C++、Delphi 还是其他 COM 兼容语言进行开发的。您编写的对象
可以用在本地计算机上或远程使用,而不用重新连编您的组件或组件的客户程序,这多亏
了 COM 和 DCOM 的魔力。而且如果您想要让您的方案在非 Windows 平台上运行,COM 正
越来越成为一种合适的手段,所以很值得认真地探究和考虑一下。
下一步:您应该知道的 COM 基本概念
下周我们将探讨 COM 的基本概念:对象、接口和模块。而且,如果有时间,我们将深入
研究一个简单 COM 对象的 C++ 代码(如果没有时间,就下一周再说。)
--
路漫漫兮,其修远。
吾将上下而求索。
※ 修改:.haojs 于 Sep 3 08:01:24 修改本文.[FROM: bbs.hit.edu.cn]
--
※ 转寄:.武汉白云黄鹤站 bbs.whnet.edu.cn.[FROM: bbs.hit.edu.cn]
--
☆ 来源:.哈工大紫丁香 bbs.hit.edu.cn.[FROM: haojs.bbs@bbs.whnet.]
Powered by KBS BBS 2.0 (http://dev.kcn.cn)
页面执行时间:3.619毫秒