问题

怎么通俗的解释COM组件?

回答
想象一下,咱们在搭积木,但不是那种小朋友玩的塑料积木,而是更高级、更专业的“软件积木”。

COM 组件,说白了,就是一种“软件积木”的标准。

你可能听说过 Windows 系统里有很多小功能,比如显示一个窗口、播放一个声音、连接打印机,这些功能都不是“一下子”写出来的,而是由很多个独立的小程序块负责的。COM 组件就是让这些小程序块能够“合作”起来的标准。

为什么需要“标准”?

你想想,如果我们玩积木,每个积木的长宽高、接口(就是连接的地方)都不一样,那是不是很难搭出像样的东西?软件也一样。如果每个小程序块(也就是我们说的“组件”)都按自己的想法来,互相之间不知道怎么说话,那操作系统和上面的各种软件就没法好好工作了。

COM 就是一个约定好的规则,告诉这些“软件积木”:

1. 你们长什么样? (也就是定义了接口,就像每个积木都有一些固定的凹槽和凸起)
2. 你们怎么跟别人打招呼? (也就是定义了如何互相调用,如何传递数据)
3. 你们要怎么把自己“注册”到系统里? (就像把积木放到一个公开的架子上,别人要知道去哪里找)

更具体地说,COM 组件就像是一个“包装盒”:

里面装着能干活的代码: 这个代码就像是盒子里的工具,可以是播放音乐的、可以是处理图片的、可以是和数据库打交道的,总之,它有具体的功能。
盒子外面贴着“使用说明”: 这个“使用说明”就是 COM 定义的“接口”。就像你买电器,包装盒上会写明电源插孔在哪儿,按钮怎么用。有了这个说明,其他程序就知道怎么“插”进来,怎么“按”按钮来使用盒子里的工具,而不用关心工具是怎么造出来的。

打个比方,让咱们更明白:

想象你是一个画家,想画一幅画。你只需要画笔、颜料、画布。你不需要自己去造画笔的毛、去提炼颜料的化学成分,或者去砍树做画布。你只需要知道怎么从商店买到符合你要求的画笔、颜料和画布,然后怎么用它们。

在这个比喻里:

你(画家):就是操作系统或者一个应用程序。
画笔、颜料、画布:就是各种 COM 组件。
商店:就是 Windows 系统或者软件安装目录,组件就“注册”在那里,供大家使用。
“符合你要求的”:意思是你需要特定型号的画笔(比如 0 号圆头笔),或者特定颜色的颜料(比如镉红)。COM 组件也一样,你只需要某个特定功能的组件,比如“播放 MP3”的组件。
“知道怎么用”:就是通过 COM 定义的接口,知道怎么调用这个组件的“播放”功能,怎么把 MP3 文件的数据传给它。

COM 组件有什么好处?

代码复用: 别人写好的功能,我可以直接拿来用,不用重复造轮子。就像我不用自己制造一根画笔,可以直接买一根。
独立性: 一个组件坏了,不一定会影响整个系统。就像你一个画笔坏了,换一根就是了,不会影响你的画架和颜料。
扩展性: 我可以随时添加新的组件,来增强我的应用程序的功能。就像我想用油画颜料,就可以买油画颜料,不用改变我用画笔和画布的方式。
语言无关: 理论上,用不同编程语言写出来的 COM 组件,只要遵守 COM 的规则,就可以互相调用。就像一个用 C++ 写的组件,可以被一个用 VB 写的程序调用。

COM 组件的历史地位和一些“缺点”:

COM 是微软在 90 年代推出的一项技术,在当时非常重要,为 Windows 应用程序的发展奠定了基础。很多经典 Windows 程序的功能都是通过 COM 组件实现的。

但是,随着技术的发展,COM 也有一些不太方便的地方:

注册表依赖: COM 组件需要注册到 Windows 的注册表里,管理起来有点麻烦,而且注册表多了也容易出问题。
版本管理: 组件更新了,可能会影响到依赖它的老程序,叫做“DLL Hell”(DLL地狱),就是因为组件版本管理不够灵活。
跨进程通信效率: 当 COM 组件运行在不同的应用程序里时,数据传递会比较慢。

总结一下:

COM 组件就像是一个 “功能模块”的标准化接口和发布机制。它让开发者可以把代码打包成可复用的、可互相调用的“软件积木”,方便操作系统和应用程序之间进行交互,也方便了软件的扩展和维护。虽然现在有 .NET、Web Services 等更新的技术,但 COM 的思想和一些核心机制依然影响着现代软件开发。

你可以把它理解成一种 “事先约好说话方式的软件服务商”。你只需要知道你的需求是什么,然后找到那个能提供这个服务的“服务商”(COM 组件),按照他告诉你的方式(COM 接口)提交你的请求(数据),他就会给你办好。你不用关心他内部是怎么工作的,只需要知道他能提供什么,怎么跟他沟通就行了。

网友意见

user avatar

解释不解释也都是死掉了的技术了啊……

COM主要是一套给C/C++用的接口,当然为了微软的野心,它也被推广到了VB、Delphi以及其他一大堆奇奇怪怪的平台上。它主要为了使用dll发布基于interface的接口。我们知道dll的接口是为了C设计的,它导出的基本都是C的函数,从原理上来说,将dll加载到内存之后,会告诉你一组函数的地址,你自己call进去就可以调用相应的函数。

但是对于C++来说这个事情就头疼了,现在假设你有一个类,我们知道使用一个类的第一步是创建这个类:new MyClass()。这里直接就出问题了,new方法通过编译器计算MyClass的大小来分配相应的内存空间,但是如果库升级了,相应的类可能会增加新的成员,大小就变了,那么使用旧的定义分配出来的空间就不能在新的库当中使用。

要解决这问题,我们必须在dll当中导出一个CreateObject的方法,用来代替构造函数,然后返回一个接口。然而,接口的定义在不同版本当中也是有可能会变化的,为了兼容以前的版本同时也提供新功能,还需要让这个对象可以返回不同版本的接口。接口其实是一个只有纯虚函数的C++类,不过对它进行了一些改造来兼容C和其他一些编程语言。

在这样改造之后,出问题的还有析构过程~MyClass()或者说delete myClass,因为同一个对象可能返回了很多个接口,有些接口还在被使用,如果其中一个被人delete了,其他接口都会出错,所以又引入了引用计数,来让许多人可以共享同一个对象。

其实到此为止也并不算是很奇怪的技术,我们用C++有的时候也会使用Factory方法来代替构造函数实现某些特殊的多态,也会用引用计数等等。COM技术的奇怪地方在于微软实在是脑洞太大了,它们构造了一个操作系统级别的Factory,规定所有人的Interface都统一用UUID来标识,以后想要哪个Interface只要报出UUID来就行了。这样甚至连链接到特定的dll都省了。

这就好比一个COM程序员,只要他在Windows平台上,调用别的库就只要首先翻一下魔导书,查到了一个用奇怪文字写的“Excel = {xxx-xxx-xxxx...}”的记号,然后它只要对着空中喊一声:“召唤,Excel!CoCreateInstance, {xxx-xxx-xxxx...}”

然后呼的从魔法阵里面窜出来了一个怪物,它长什么样我们完全看不清,因为这时候它的类型是IUnknow,这是脑洞奇大无比的微软为所有接口设计的一个基类。我们需要进一步要求它变成我们能控制的接口形态,于是我们再喊下一条指令:

“变身,Excel 2003形态!QueryInterface, {xxx-xxx-xxxx...}”

QueryInterface使用的是另一个UUID,用来表示不同版本的接口。于是怪物就变成了我们需要的Excel 2003接口,虽然我们不知道它实际上是2003还是2007还是更高版本。

等我们使唤完这只召唤兽,我们就会对它说“回去吧,召唤兽!Release!”但是它不一定听话,因为之前给它的命令也许还没有执行完,它会忠诚地等到执行完再回去,当然我们并不关心这些细节。

微软大概会觉得自己设计出了软件史上最完美的二进制接口,从今以后所有的第三方库都可以涵盖在这套接口之下。然而历史的车轮是无情的,它碾过那些自以为是的人的速度总是会比想象的更快。Java的直接基于类的接口被广泛应用,开发使用起来远远来的简单,即便偶尔出点问题大家也都想办法解决了,事实证明程序员并不愿意花10倍的编写代码的时间来解决二进制库的版本兼容问题,他们更愿意假装没看见。很快微软也抄了一个.NET托管dll的方案出来,于是纯的二进制接口COM就慢慢被抛弃了。

COM,OLE,ActiveX,OCX,VBScript,历史不会忘记你们的,如果历史忘了,我替历史记住你们。安息吧。

=============================================================

这怎么还火了,这不应该是一个讨论遗留技术的冷门问题吗……

补充说明一下,其实并没有贬低COM的意思,COM在当时一定是一个伟大的发明,只是技术革新太快。到今天,其实COM都是C++的二进制本机代码的动态链接的最佳的选择(Windows上的)。但是,C++,二进制本机代码,动态链接,这三件事现在没有一件是重要的……

COM在Windows操作系统底层继续发挥余热的时间肯定还会很长,因为永远都会有偏底层的C++开发的需要,必须游戏之类。只是技术趋势已经很明确了。

类似的话题

本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度google,bing,sogou

© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有