本文主要介绍 COM 的基础知识,倾向于理论性的理解,面向初学者,浅尝辄止。
1. COM 是什么:
COM 的英文全称是,Component Object Model,中文译为,组件对象模型。它官方的概念是:The Microsoft Component Object Model (COM) is a platform-independent, distributed, object-oriented system for creating binary software components that can interact. 与其说 COM 是 System,更确切的说,应该叫做 Standard。因为它实际上是一套公共的 binary 标准,用于规定 software component 的接口的标准。也有人会把 COM 叫做 Common Object Model,但这其实是一种误传,起源于当初一名微软的工作人员 Mark Ryland[1]!
但是,在长期的使用中,大家口中念叨的 COM 渐渐生出了各种各样的含义,已经不仅仅是最初的含义了。这也是为什么,对于初学者来说,理解起来特别费劲。因为总会查到层出不穷,又参差不齐的含义,让人很疑惑到底哪个含义才是标准!
- 首先,在系统设计上,它是一种,设计理念,
- 然后,在 Object 的世界中,它是那个世界中的,规范和标准,
- 再然后,在编程语境下,它是一种,可以调用的,其他程序的接口,
- 再然后,在实际的电脑文件中,它多数是一个个 DLL 文件,
- 最后,在内部性质上,它是一个个二进制(Binary)的小程序,
其实在我 Research 的时候,这些概念也是最头疼的地方。因为对 COM 概念的不了解,所以无法区分在不同视角,或者不同角度下的概念。而在 Research 的时候,往往会看到很多不同的网页,给出各种 COM 的概念。而让人头疼的就是,这些概念都不统一,让人无法理解。甚至开始怀疑,这些网页真的都是说的同一个东西的概念么?这种混乱,就是因为,不同的网站,在解释 COM 的时候,用的是不同的角度。而同一个东西,在不同的角度和情景之下,自然会有不同的含义。所以,往往 COM 到底是什么,要因情景而视之!但是,上面列出的几个角度,已足以让我们对 COM 的概念,构建出一个较为立体的理解了!
2. 科技历史与 COM 的由来:
要了解 COM 的历史由来,我们首先要从科技发展的历史开始捋顺下来。先说 Object Oriented 这种概念,其实,这种概念据说是19世纪60年代就有了,是源自于 MIT。但是那个时候 PC 还没有普及,大多数公司使用的都是十分昂贵的大型机,微软还没有成立,这种情况下,自然对软件的需求也是非常基本的,大多数停留在数学计算,文字处理,和军事使用的范围。而且那个时候,CPU 的概念都还没出现,自然电脑也是不可能具备足够的运算能力的。第一个 CPU,是 Intel 在 1971 年,为完成一家日本公司的订单而设计发明的,自此才解锁的计算的巨大潜力,进入了现代计算机的篇章。而 CPU 的发展与成熟,才造就了后来的软件行业。微软也于 1975 年正式成立。同年,IBM 也开始生产 Portable Computers。那时起,微型电脑开始变成潮流。Microsoft BASIC 这种最早期的编程语言,也诞生于 1975年。那时候,绝大多数的微型电脑,虽然系统都不同,但是几乎都支持 BASIC 这种编程语言。正是这种编程语言,成为了日后微软的基础。1981年,微软首次为 IBM 提供了操作系统,叫做 QDOC,是微软的第一套系统,但却是买来的,别人研发的系统。1983年,微软研发出了 Lotus Software,是最早期的电子表格软件,这个软件成为了 IBM 电脑上的明星软件,装机必备。后来 1985年 Windows 1 诞生了,同时伴随着 Microsoft Excel 的诞生。自此之后,软件行业开始变得日益繁荣,微软也开启了自己的软件帝国。
软件行业变得日益繁荣,但是 Object Oriented 这种思想,还并没有发展到一定的高度。那时候,开发个软件或者系统是非常麻烦,非常复杂的。日后,想给软件添加一个功能,或者升级一个功能时,就更麻烦,因为,没办法独立的,去更新,或者改进,某一个特定的功能。若想要添加新功能,需要全盘改动后,再重新进行编译,非常的费时费力。于是人们就开始想办法解决,上面这个问题,然后 Object Oriented Programming(面向对象的编程)的概念就开始兴起。这时,虽然面向对象编程的概念已经兴起,但是,还没有统一的 Framework 或者说是标准,能让不同软件之间里的 Object 可以互相交流。于是,不同的软件,就变成了一个个,孤立在大海中的小岛,里面住着一堆 Objects,无法和外界交流。人们想出的解决办法就是,开发一个系统或者体系,在这个体系中,写软件的人只需要制造出,Software Component 就行了。而,这个 Component 就像是,买回来一个,方块形的,电子配件一样,插在我们自己的电路板上,就可以开始发挥作用。而这些,Software Component 外表上,必须遵循系统中的统一标准,而内部,就随便怎么编程都可以。后来,微软在1993年,开发出了这个标准,这标准就被叫做,Component Object Model (COM)。
我们再把 COM 形成前后的,技术发展,掰开来看下。首先,在 1987年,也就是在 Windows 2 发行的时候,Dynamic Data Exchange (DDE) 技术产生了,作为一种进程间通信手段(Inter-process communication)。在这个技术之前,只支持系统和客户端应用程序之间的通信,这个通信,则是通过 Windows Messaging Layer 实现的。而 DDE 技术,则再此基础之上,实现了客户端应用程序之间的通信,这也就成为了进程间通信技术的开端。但这时的通信,还只是停留在 Text conversations 和 Windows messages 的层面上。之后 Antony Williams 分别于 1988年 和 1990年 发表了两篇微软内部文章,分别是 【Object Architecture: Dealing With the Unknown】和【On Inheritance: What It Means and How To Use It】。正是两篇文章奠定了日后 COM 产生的理论基础。之后,1991年,微软在 DDE 的基础上,开发出了第一个 object-based framework(基于对象的框架),叫做 Object Linking and Embedding (OLE),即对象的连接与嵌入。这个技术是同 Word 软件的发行一同发布的,专注于实现 Compound document(复合型文件),就是将 Excel 文件,嵌入到 Word 中。同在 1991年,微软发布了 Visual Basic 1.0 编程语言,并以 dynamic-link library (DLL) 的形式,附带了 Visual Basic Extensions (VBX) 插件。这个插件让使用者,可以通过 Properties and Methods 来操控 objects (对象)。1992年,随着 Windows 3.1 的发布,OLE2 和它自己的 Object Model,也一同发布了。和 OLE1 不同,OLE2 是在 COM 概念的基础上重新实施的。这时的 COM Application binary interface (ABI),即 COM 应用接口的标准也发生了改变。1994的时候,微软宣布,OLE2 改名为 OLE,从此 OLE 变成了,微软组件技术(Component Technologies)的统称。同年还发布了 OLE Custom Controls (OCXs) ,作为 VBX 的升级版。后来,在1995年,微软发布了 Visual Basic 4.0,开始支持 OCXs,这时微软也开始考如何让 COM 组件可以实现跨语言支持。这就要求,COM 架构下,必须要提供一个一致的接口,以及提供一组可以调用接口内方法的能力。此后,才发展成了我们现今熟悉的 COM 的含义。在1996年,微软有发现 OCXs 可以应用在浏览器上,所以就把部分 OLE 改名为 Internet "ActiveX",然后,逐渐的,所有的 OLE 都改名叫做 ActiveX 了。同年后期,微软又拓展了 COM 的能力,使得组件对象,拥有了在网络上通信的的能力。这个技术被称为 DCOM (Distributed COM)。自此便形成了今时今日的 COM 的概念。
这篇文章中涉及到了不少的历史,之前在学习,近200年的,科技发展历史时,把 Technology Timeline 写在博客里,感兴趣的同学可以点进去看一下。在那篇博客中,我把微软技术发展的 Timeline,和科技发展的 Timeline 揉合在了一起,这样能帮助我们感觉到,技术是在伴随着科技的发展而发展。也可以看到,COM 这种技术理念的产生,是在PC渐渐普及起来,操作系统渐渐成熟,软件需求逐渐提高之后才有的。所以很多事情的发展,往往都是自然而然的,因为有了需求,才会有相应的发展。COM 的产生,从这个角度说,也是一种必然。因为 COM 实际上,就是一种规范,就像行业规范一样,行业太小自然不用规范,而规模大了,需求大了,自然会产生规范,所以 COM 的产生是一种必然结果。而技术的发展,既在推进着科技的发展,也受制于现有科技的边缘。
3. 接口(Interface)周围的概念:
个人认为,关于 COM,的一切的一切,最后都是为了这个“接口”。因为只有有了“接口”,才算是真正的实现了 Component Object 的理念(或者说,才算是实现了,把 Object 变成 Component 的想法)。因为只有有了“接口”,Component 才能被叫做组件,才能被调用。所以下面简单描述下,几个常见的,围绕着“接口”的概念:
3.1. API (Application Programming Interface)
API 中文为,应用程序编程接口;很多时候,都被直接叫做 “接口” (Interface)。“接口”是“操作系统” 或 “程序库”,提供给应用程序的,接入点,让应用程序能调用,系统某一方面的功能。其主要作用是,让开发人员,可以轻松调用这些功能,而不需要了解,到底怎么做到的,和底层代码。但是,API 并不是代码,他只是一个接口,或者说只是一个地址。
3.2. IDL(Interface Defining Language)
IDL 中文为,接口描述语言。它是一种 Specification language(规范语言)。是用来描述,软件 Component 的 API 的 “规范语言”。与之相比,Programming Language(编程语言)是可以直接运行,用于系统实现的,形式语言。而,“规范语言” 是通常不能直接运行的,而是用于系统分析和设计的,描述语言。然后,这些一段一段的用来描述 Interface 的文字,就被储存在了 IDL 文件中(文件后缀就是 “.idl”)。每个这样的文件里面都有,一个 header 和一个 body。格式很整齐。
3.3. OLE (Object Linking and Embedding)
OLE 中文为,对象链接与嵌入。是能让应用程序创建,包含不同来源的文档,的复合文档技术。OLE 是建立在 COM 理念的基础之上的。COM 是理论框架,而 OLE 是根据这个框架,实施出来的一套技术。正如之前在,PC Mag 杂质上看到的一个副标题,就很好的诠释了这种关系型:"COM-the master plan that lets Windows apps interact through OLE.[2]" 一个比较常见的例子就是,把 Excel 表格,整个插入到 Word 文档中。而这个 Excel Object,就是通过这个 OLE 技术,连接并嵌入(Linking and Embedding)到 Word 中的。但,正如上文所说,后来 OLE 就变成了,微软组件技术(Component Technologies)的统称。而,像这样的一个典型的 OLE 功能(或 OLE 特性),其背后是有一大堆,Interface(接口)作为支持,才得以实现的。
3.4. GUID (Globally Unique Identifier)
GUID 中文为,全局唯一标识符,也被称为,UUID (Universally Unique Identifier),即,通用唯一识别码。是一个用于标识信息的,128-bit的,标识符;由一组,32位數的,16进制的,数字组成。数字串,的格式为 8-4-4-4-12 的32个字元;大概长成这样:550e8400-e29b-41d4-a716-446655440000。GUID 具有全球唯一性,出现重复的概率几乎为零。所以才叫做,全局唯一标识符。
3.5. CLSID (Class IDentifier)
CLSID 中文为,类标识符。是一个 GUID,但是,是专门用于,标识 COM class object 的。每一个 OLE Class,都会有一个与之对应的,全球独一的 CLSID。
3.6. ProgID (PROGrammatic IDentifier)
ProgID 可以翻译为,程序标识符,或者,编程标识符。通常情况下,每个 CLSID,都会有对应的 ProgID。例如,ProgID 是 Msxml2.DOMDocument 的字符串;而 CLSID 则长成这个样子 {F9043C85-F6F2-101A-A3C9-08002B2F49FB}。所以 ProgID 的存在,是为了编程的时候,方便调用。
3.7. Registry (Windows)
Registry 中文为,注册表。它是一个 hierarchical database (分层的数据库)。用于储存 Windows 系统,或者应用程序的 low-level settings。但并不是,所有的应用程序都选择把设置,存在注册表里。Registry 是从 Windows 3.0,推出 OLE 的时候,开始有的。在那之前,应用程序都是把自己的设置信息存在一个后缀为“.ini”的文本文件中的。除了设置之外,所有的 OLE Objects 的 CLSID, 也都储存在这个 Registry 中。感兴趣的同学可以去 Regedit.exe 中,这个位置(HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID)看下,所有的 CLSID 都长成什么样。而在每个 CLSID 的“文件夹”中,又都会储存着,这个 Class 的 ProgID 的名字。
4. 调用组件的流程
阅读到这里的同学,应该已经对基本的概念有一些了解。那么我们就再来看下,在实际运行中,Component(组件)到底是怎么被调用的。就用在 Word 里面插入一张 Bitmap Image 来举例;当我们在 Word 程序里,点击插入 Object 的时候,Word 会弹出,插入对象的对话框。然后,程序会立刻开始扫描,系统的 Registry,看哪些 Object 是能够插入的,然后,把它们都列在你面前,供你选择。当你选中 Bitmap Image 这个对象后,程序会,立刻调出他的 ProgID 名字,然后,把这个名字,递给一个叫做 CoCreateInstance 的 OLE Function。再然后,在这个 Function 的内部,就开始了一系列流程。
首先 OLE 会用立刻这个 ProgID(或者 CLSID)去找出与它对应的 Regstry 条目。然后,通过注册表里登记的信息,便可以轻易的找到,提供这个“服务”的,应用程序,或者 Component(.exe 或者 .dll)到底住在哪里。一旦找到了,便可以立刻调用(Invoke)这个 Component,然后创建出,一个 Bitmap 对象的 Instance(这个Instance,似乎也叫做 Interface Pointer)。
这和我们写代码的时候是一样的,在我们要使用某个模块的功能之前,都是先通过 PorgID 创建一个 Instance(例如,在 VBScript 中,通常是用Set XL = CreateObject("Excel.Application")
这样的语句)。然后通过这个 Instance,和它提供的 Interface(接口),我们就可以开始调用,这个实例的各种属性和功能了(例如 XL.Workbooks.Open("FilePath")
)。
当然,这个流程中省略了很多的细节,想要了解具体细节的同学,就去看我“脚注2”中的 Article 吧,这篇文章虽然是1995年的,但是写的非常的好,非常值得阅读,尤其是在学习 COM 的同学,这篇文章能很好的提升我们的理解。好吧,这部分就这么多啦。
5. 篇尾结语:
研究的过程中发现,COM 相关的东西,真的是博大精深,作为一只小白,也只能是浅尝辄止了。感谢您花时间阅读,小白贡献,语失莫怪。文章中涉及到的历史并没有列在这里的参考阅读,而是列在了我另一篇叫做 Technology Timeline 的博客里,有需要的同学可以点进去看下 (^_−)☆。
P.S. 为了写完这篇,关于 COM 的小白文章,真的是有一种,跋山涉水,翻山越岭的感觉啊 (T^T),希望能对同学们有所帮助....
相关书籍:
- 《COM技术内幕》,Dale Rogerson
- 《Learning DCOM》,Thuan L. Thai
- 《Inside OLE》,Kraig Brockschmidt
- 《COM本质论》,Don Box
- 《MFC Windows程序设计》,Jeff Prosise
- 《Windows XP Under the Hood》,Brian Knittel
参考阅读:
- 组件对象模型_百度百科
- Component Object Model - Wikipedia
- COM Technical Overview - Win32 apps | Microsoft Docs
- The Component Object Model - Win32 apps | Microsoft Docs
- COM - Marvin's Blog
- PC Mag - Google Books
详见《COM本质论》;作者:Don Box;来源:Charlie Kidel 序。 ↩︎
[PC Mag], 21 Nov 1995, Vol. 14, No. 20, ISSN 0888-8507, Article: OLE's Component Object Model ↩︎