本页使用了标题或全文手工转换

组件对象模型

维基百科,自由的百科全书
跳转至: 导航搜索

组件对象模型,英文为Component Object Model,縮寫COM,是微軟的一套软件组件二进制接口标准。这使得跨编程语言的进程间通信、动态对象创建成为可能。COM是多项微软技术与框架的基础,包括OLE, OLE自动化英语OLE Automation, ActiveX, COM+, DCOM, Windows shell英语Windows shell, DirectX, Windows Runtime.

概要[编辑]

COM实质上是一种语言无关的对象的实现方式,使其可以在创建环境不同的场合被复用,甚至是跨计算机的分布环境。COM允许复用这些对象而不必知道对象内部是如何实现的,因为组件实现者必须提供良好定义的界面从而屏蔽了实现细节。通过引用计数,组建对象自己负责动态创建与销毁,从而屏蔽了不同编程语言的内存分配语义的差异。在COM界面(interface)之间的类型转换,通过QueryInterface方法。

对于某些应用程序,COM已经部分被Microsoft .NET框架取代。COM通过Windows Communication Foundation (WCF)支持Web Service。COM对象通过.NET COM Interop可以被所有.NET语言使用。网络化的DCOM使用二进制私有格式,而WCF鼓励使用基于XMLSOAP消息机制。COM非常类似于其他软件组件界面技术,如CORBAJavaBeans,各自有其优点与弱点。

与C++不同,COM提供了一个稳定的应用二进制接口(ABI),不随编译器版本而变。

歷史[编辑]

早在1988年,微软的Anthony Williams论文“Object Architecture: Dealing with the Unknown or Type Safety in a Dynamically Extensible Class”以及1990年论文“On Inheritance: What It Means and How To Use it”,奠定了COM的理论基础。

Windows作業系統提供了三種进程間的通訊機制:剪貼簿(clipboard)、DDEOLE。OLE原名是物件連結與嵌入(Object Linking and Embedding),OLE可說是DDE的改良版。1992年,随Windows 3.1操作系统发布了了OLE 1.0版,提供复合文件(compound document)处理,但過於複雜,Brockschmidt, Kraig「Inside OLE」一书中提到,必须经过六个月的心灵混沌期,才能了解OLE是什么。1993年,随OLE 2.0第一次公开发布了COM架构。所有OLE元件皆是COM繼承而来的,這些技術包含OLE Document和OLE Controls、Drag and Drop等。

1996年,为应对CORBA,发布了DCOM。随Windows 2000发布了COM+,关注MTS;并放弃了DCOM这个名称。

COM元件類型[编辑]

COM是基於元件物件方式概念來設計的,在基礎中,至少要讓每個元件都可以支援二個功能:

  • 查詢元件中有哪些界面
  • 讓元件做自我生命管理,此概念的實踐即為引用計數(Reference Counting)

這二個功能即為COM的根:IUnknown介面所提供的IUnknown::QueryInterface()IUnknown::AddRef()IUnknown::Release()三個方法的由來。所有的COM元件都要實作IUnknown,表示每個COM元件都有相同的能力。

只由COM衍生實作出來的元件,稱為純COM元件

但在Windows持續發展時,Visual Basic 4.0開始支援OCX,也就是OLE Custom Control,這讓微軟開始思考要如何讓COM元件可以跨語言支援,在這樣的要求下,必須要提供一個一致的介面,以及提供一組可以呼叫介面內方法的能力,由於純COM元件只能夠支援C/C++的直接存取,為了要達到跨語言的能力,在COM中必須要支援在外部呼叫內部方法的機能,這個機能造就了Invoke()方法,另外為了跨語言的支援,COM應該要提供簡單的元件存取識別方式,這也就是會有GetIDsOfNames()的原因,將這些方法組合起來,定義出的必要介面,稱為IDispatch介面,所有實作此介面的,都可以支援跨語言的支援。

微軟將實作此介面的元件都稱為自動化(Automation)元件。

相關技術[编辑]

COM曾是Windows平台下主要的軟體開發平台,並且影響至其他許多相關軟體技術。

COM+[编辑]

COM+是微軟Windows 2000中,Microsoft Transaction Server的強化實作版本,除了提供基本的元件交易支援外,還提供了鬆散藕合式事件(loosely-coupled events)與物件共用池(object pooling)等應用程式伺服器的能力,成為Windows 2000開始在微軟平台上主要的應用程式伺服器平台,目前.NET Framework也提供了System.EnterpriseServices命名空間以支援COM+。

Distributed COM[编辑]

Distributed COM是可以在網路上通訊的COM元件,依據RPC(Remote Procedure Call)的規範來發展的,它將COM元件的能力擴及到網路上,但是因為網路安全以及防火牆的問題,讓DCOM無法廣泛的流行。

.NET[编辑]

.NET Framework是新一代的Microsoft Windows應用程式開發平台。

技术细节[编辑]

不同的COM组件类型用类ID(CLSID)标示,这是一种全局唯一标识符(GUID)。每个COM组件用一个或多个接口来暴露其功能。这些接口也用GUID唯一标识,称为接口ID(IID)。

COM接口与几种编程语言有语言绑定,如C语言, C++, Visual Basic, Delphi语言, Python[1][2]以及Windows平台上的几种脚本语言。都是通过接口的方法来访问组件。

接口[编辑]

所有COM组件都实现了IUnknown英语IUnknown接口,该接口暴露了引用计数实现的对象生命期管理与类型转换英语type conversion以访问不同的预定义的接口。

IUnknown接口以及基于IUnknown的定制的接口,包括一个指向虚函数表英语virtual method table的指针,虚函数表中包含若干函数指针,分别指向接口所声明的函数实现。对于进程内的COM组件调用,其效率等同于C++的虚函数的调用。

除了基于IUnknown的定制的接口,COM也支持继承自IDispatch英语IDispatchdispatch接口,从而支持了用于OLE自动化英语OLE Automation晚绑定英语late binding。很多不能访问定制接口的编程语言,可以通过dispatch接口访问COM组件,如VBS

[编辑]

COM类(coclass)是一个或多个接口的具体实现,很类似于面向对象程序设计语言中的类。使用GUID标识类,称作类ID(CLSID);或者programmatic identifier字符串(progid),因为VBS等脚本语言不能使用GUID,只能用字符串查找、使用COM组件。

COM对象不能直接被访问,只能通过COM接口来访问对象。COM也支持同一个接口的多个实现,因此客户程序运行时可以选择实例化接口的哪个实现。

接口定义语言与类型库[编辑]

类型库(type library)包含着COM类型的元数据。这些类型用微软接口定义语言(MSIDL/IDL)英语Microsoft Interface Definition Language描述。

IDL文件定义了类、接口、结构、枚举与其他用户定义类型。IDL类似于C++的声明,使用了一些额外的关键字如"interface"、"library"等。IDL还支持在声明前给出方括号属性(bracketed attribute)以提供额外信息,如接口的GUID、指针参数与长度域之间的关系等。

MIDL编译器用来编译IDL文件,产生编译器独立的头文件。头文件包含了IDL文件中声明的接口对应的结构定义。结构只包含一项成员,即指向在接口中声明函数的地址表的指针(vtbl),以模仿C++对虚函数的实现。头文件还包含了类与接口等的GUID的常量的定义。MIDL编译器也可以产生C++源文件,包含代理模块(proxy module),用以把COM调用转为远程过程调用,以支持跨进程的DCOM通信。

IDL文件也可以被MIDL编译器生成类型库(TLB)文件.TLB,以供其他语言编译器与运行时环境使用,如VBDelphi.NET等生成语言相关表示COM类型的结构。C++把TLB转回到IDL表示。

COM作为对象框架[编辑]

COM是一个运行时框架,类型必须在运行时单独地标识并可指定。为此,使用GUID,每个COM类型被指定了它自己的GUID用于运行时标识。这也解决了C/C++语言的名字修饰导致的链接兼容性问题。

为了解决COM类型信息在编译时与运行时都可以访问到,COM使用类型库。这使得COM成为对象交互的动态框架。

考虑下述用IDL定义coclass的例子:

coclass SomeClass {
  [default] interface ISomeInterface;
};

上述代码框架声明了一个COM类,称为SomeClass,实现了接口ISomeInterface

这在概念是等价于下述C++类:

class SomeClass : public ISomeInterface {
  ...
  ...
};

其中ISomeInterface是一个C++虚基类

包含COM接口与类的IDL文件被编译为类型库(TLB)文件。客户程序可以在运行时分析类型库文件,以确定对象支持哪些接口,然后调用对象的接口方法。

C/C++程序以类ID(CLSID)与接口ID (IID)作为参数,用CoCreateInstance函数实例化COM对象。SomeClass的实例化代码如下:

ISomeInterface* interface_ptr = NULL
HRESULT hr = CoCreateInstance(CLSID_SomeClass, NULL, CLSCTX_ALL,
                              IID_ISomeInterface, (void**)&interface_ptr);

在这个例子中,使用COM子系统获取指向ISomeInterface接口的实现对象的指针,用CLSID_SomeClass指示用这个特定的coclass。

引用计数[编辑]

所有COM对象利用引用计数管理对象的生命期。客户程序通过所有COM对象都要强制实现的IUnknown接口的AddRefRelease方法来控制引用计数。当引用计数降到0时,COM对象自己负责释放内存。

特定语言(例如Visual Basic)提供了自动引用计数,所以COM对象开发者在源代码中不需要显式维护任何内部的引用计数。C++编程者或者执行显式的引用计数或者使用智能指针自动管理引用计数。

下述是如何调用COM对象的AddRefRelease的指引:

  • 函数、方法返回接口的引用(通过返回值或者"out"参数),应当在返回前增加被返回的对象的引用计数。
  • 接口指针被覆盖或超出作用域之前,必须调用接口指针的Release方法。
  • 如果一个接口引用指针被复制,必须调用该指针的AddRef方法。
  • AddRefRelease必须在被引用的相关接口上调用。因为一个COM对象可能实现了逐个接口上的引用计数,使得仅在相关接口上内部分配资源。

不向远程对象发出引用计数的调用。代理模块保持着远程对象的一个引用,并维持着它自己的本地引用计数。

为简化COM开发,引入了活动模板库(Active Template Library,ATL)用于C++开发。ATL提供了更高层次的COM开发范式。ATL也有益于COM客户应用程序开发摆脱直接维护引用计数,而是用智能指针对象。

其它能直接支持COM的库与语言还包括MFCVisual C++编译器的COM支持,[3] VBScriptVisual BasicECMAScriptJavaScript)、Borland Delphi等。

程序设计[编辑]

COM是一个语言独立的二进制标准,任何能够理解与实现COM的二进制定义的数据类型与接口的语言都可以开发COM组件。

COM实现负责进入、离开COM环境,实例化与引用计数COM对象,查询对象支持的接口,以及错误处理。

Microsoft Visual C++编译器支持对C++语言的扩展:称作C++ Attributes[4]这些扩展被设计用于简化COM开发,去除实现COM服务器时大量臃肿的代码。[5]

使用注册表[编辑]

在Windows操作系统中,COM类、接口、类型库都根据其GUID登记到Windows注册表中。HKEY_CLASSES_ROOT\CLSID下是COM类;HKEY_CLASSES_ROOT\Interface下是接口。COM类型库注册在每个COM对象的本地库条目下或者远程服务的网络位置处。

不使用注册表的COM[编辑]

不使用注册表的COM(RegFree COM)是Windows XP引入的技术,允许COM组件不在注册表中存期激活的元数据与类ID(CLSID),而是在实现类的assembly manifest英语Manifest (CLI)或者存储在可执行文件的资源中或组件安装时的单独文件中。[6]这使得同一组件的不同版本可以安装在不同目录下,用其各自的manifest描述,直接复制安装英语XCOPY deployment[7]这种技术有限支持EXE COM服务器[8]且不能用于系统范围组件如MDACMSXMLDirectXInternet Explorer

应用程序装入时,Windows装入器搜索manifest。[9]如果存在,装入器从它增加信息到激活上下文。[7]COM类工厂试图实例化一个类时,激活上下文首先检查这个CLSID的实现是否可以找到。仅当查找失败时,才扫描Windows注册表[7]

进程与网络透明[编辑]

COM对象可以透明地实例化与引用在同一进程、跨进程边界、甚至在网上远程(DCOM)。进程外或远程对象用marshalling序列化方法调用与返回值。这种marshalling对用户是不可见的,就如同访问进程内的COM对象。

线程化[编辑]

在COM中,线程通过apartment寻址。[10]所有COM对象存在于一个apartment,既可以是单线程的也可以是多线程的。COM中有3类apartment:单线程英语Single threadingApartment (STA)多线程Apartment (MTA),以及Thread Neutral Apartment(NA)。每一种apartment表示对象内部如何在多线程下同步的机制。一个进程可以包含多个COM对象,其中一些可以用STA,其它可以用MTA。所有线程以类似方式访问同一apartment中的COM对象。COM对象与线程的apartment的选择在运行时确定后就不能再改变。

Apartment类型 描述
单线程Apartment[11]STA),(ThreadingModel=Apartment 单线程用于执行对象的方法。apartment之外的线程调用的方法要marshalling英语Marshalling (computer science)并由操作系统自动排队(通过一个标准的Windows消息队列)。因此COM运行时提供了自动同步以确保对象的方法调用执行完毕后才能启动新的方法调用。开发者不需要担心线程上锁(locking)或競態條件
多线程Apartment[12]MTA),(ThreadingModel=Free COM运行时不提供同步,多个线程可以同时调用COM对象。COM对象因而需要执行自己的同步控制以避免多线程同时访问造成的競態條件。STA状态的线程调用MTA状态的对象也需要marshalling。
Thread Neutral ApartmentNA),(ThreadingModel=Neutral 一个特殊的apartment,没有任何指定的线程。当STA或MTA线程调用同一进程的NA对象,则主调线程临时离开它的apartment并执行对象的代码,没有任何线程切换。[13]因此NA可以认为是优化apartment之间方法调用的效率。

线程与属于同一apartment的对象遵循相同的线程访问规则。可以直接执行方法调用而不需COM的辅助。跨apartment的方法调用需要marshalling。这需要代理(proxies)与“桩”(stubs)。

除了以上模式,COM服务器也可以指定双apartment模式。即在对象创建时根据调用线程的apartment类型自动选择STA或MTA。[14]这避免了STA线程访问MTA服务器的marshaling开销。

批评[编辑]

消息泵[编辑]

STA初始化时,创建一个隐藏窗口,用于apartment之间、进程间的消息路由。该窗口必须有正常的消息队列泵。这种结构称为消息泵英语message pump。早期版本的Windows,消息泵的失败会导致系统范围的思索。这个问题被初始化COM的Windows API复杂化了,并会导致实现细节的泄露。

引用计数[编辑]

如果多个对象是循环引用英语circular reference,则可能会导致问题。

Objects may also be left with active reference counts if the 使用COM事件池(event sink)模型,则对象可能一直保持活动的引用计数而不能被销毁。因为发送事件的对象必须有处理事件的对象的引用,因而对象引用计数永远不为0.

引用循环可以采取下述技术来克服:

  • 带外终止(out-of-band termination),对象暴露一个方法,该方法调用时迫使该对象放弃对其他对象的全部引用。
  • 身份分离(split identity)技术,一个实现暴露两个单独的COM对象(也称作identity),之间保持weak reference

DLL地狱[编辑]

进程内的COM组件是用DLL文件实现,每个版本的DLL用CLSID登记到Windows注册表,因而某些情况下会发生DLL Hell效应。无需注册的COM克服了这一问题。

参见[编辑]

参考文献[编辑]

  1. ^ http://docs.activestate.com/activepython/2.4/pywin32/html/com/win32com/HTML/docindex.html
  2. ^ http://www.boddie.org.uk/python/COM.html
  3. ^ Compiler COM Support. MSDN. Microsoft. 
  4. ^ Microsoft MSDN: C++ Attributes Reference
  5. ^ MSDN Magazine: C++ Attributes: Make COM Programming a Breeze with New Feature in Visual Studio .NET
  6. ^ Assembly Manifests. MSDN. [2009-11-05]. 
  7. ^ 7.0 7.1 7.2 Dave Templin. Simplify App Deployment with ClickOnce and Registration-Free COM. MSDN Magazine. [2008-04-22]. 
  8. ^ How to use an out-of-process COM server without its tlb file. [2011-04-16]. 
  9. ^ Concepts of Isolated Applications and Side-by-side Assemblies. MSDN. [2009-11-05]. 
  10. ^ Microsoft MSDN: Processes, Threads, and Apartments
  11. ^ Microsoft MSDN: Single-Threaded Apartments
  12. ^ Microsoft MSDN: Multithreaded Apartments
  13. ^ Codeguru: Understanding COM Apartments
  14. ^ Microsoft MSDN: Understanding and Using COM Threading Models

外部链接[编辑]

Category:Component-based software engineering英语Category:Component-based software engineering Category:Object models英语Category:Object models Category:Object request broker英语Category:Object request broker