族谱网 头条 人物百科

组件对象模型

2020-10-16
出处:族谱网
作者:阿族小谱
浏览:410
转发:0
评论:0
概要COM实质上是一种语言无关的对象实现方式,这使其可以在创建环境不同的场合、甚至跨计算机的分布环境下被复用。COM允许复用这些对象,而不必知道对象内部是如何实现,因为组件实现者必须提供良好定义的接口从而屏蔽实现细节。通过引用计数,组件对象自己负责动态创建与销毁,从而屏蔽了不同编程语言之间的内存分配语义差异。在COM接口之间的类型转换通过QueryInterface方法。对于某些应用程序来说,COM已经部分被.NET框架取代。COM通过WindowsCommunicationFoundation(WCF)支持WebService。COM对象通过.NETCOMInterop可以被所有.NET语言使用。网络化的DCOM使用二进制私有格式,而WCF鼓励使用基于XML的SOAP消息机制。COM非常类似其他软件组件接口技术,如CORBA与JavaBeans,它们各自有其优点与弱点。与C++不同,CO...

概要

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

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

与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操作系统提供了三种进程间的通信机制:剪贴板、DDE与OLE。OLE原名是对象链接与嵌入(Object Linking and Embedding),OLE可以说是DDE的改良版。1992年,OLE 1.0版随Windows 3.1操作系统发布,提供复合文档(compound document)处理,但它过于复杂,Brockschmidt, Kraig的“Inside OLE”一书中提到,必须经过六个月的心灵混沌期,才能了解OLE是什么。1993年,COM架构随OLE 2.0第一次公开发布。在微软Office包中,COM取代了OLE。这成为COM技术战胜Windows 95团队开发的其他对象技术的关键因素。

1996年,为应对CORBA,DCOM随Windows NT 4 Option Pack发布。而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是依据RPC(Remote Procedure Call)的规范发展的可以在网络上通信的COM组件,它将COM组件的能力扩及到网络上,但因为网络安全以及防火墙因素,DCOM无法广泛的流行。

.NET

.NET Framework是新一代的Microsoft Windows应用程序开发平台。

ATL

WTL

技术细节

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

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

接口

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

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

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

Windows API提供了C语言定义COM接口的方法:

#include#undef INTERFACE#define INTERFACE IClassFactoryDECLARE_INTERFACE_(IClassFactory,IUnknown){// *** IUnknown methods ***STDMETHOD(QueryInterface)(THIS_REFIIDriid,LPVOIDFAR*ppvObj)PURE;STDMETHOD_(ULONG,AddRef)(THIS)PURE;STDMETHOD_(ULONG,Release)(THIS)PURE;// *** IClassFactory methods ***STDMETHOD(CreateInstance)(THIS_LPUNKNOWNpUnkOuter,REFIIDriid,LPVOIDFAR*ppvObject)PURE;};// 等效的C++例子:structFARIClassFactory:publicIUnknown{virtualHRESULTSTDMETHODCALLTYPEQueryInterface(IIDFAR&riid,LPVOIDFAR*ppvObj)=0;virtualHRESULTSTDMETHODCALLTYPEAddRef(void)=0;virtualHRESULTSTDMETHODCALLTYPERelease(void)=0;virtualHRESULTSTDMETHODCALLTYPECreateInstance(LPUNKNOWNpUnkOuter,IIDFAR&riid,LPVOIDFAR*ppvObject)=0;};// C语言宏扩展后是这样的:typedefstructIClassFactory{conststructIClassFactoryVtblFAR*lpVtbl;}IClassFactory;typedefstructIClassFactoryVtblIClassFactoryVtbl;structIClassFactoryVtbl{HRESULT(STDMETHODCALLTYPE*QueryInterface)(IClassFactoryFAR*This,IIDFAR*riid,LPVOIDFAR*ppvObj);HRESULT(STDMETHODCALLTYPE*AddRef)(IClassFactoryFAR*This);HRESULT(STDMETHODCALLTYPE*Release)(IClassFactoryFAR*This);HRESULT(STDMETHODCALLTYPE*CreateInstance)(IClassFactoryFAR*This,LPUNKNOWNpUnkOuter,IIDFAR*riid,LPVOIDFAR*ppvObject);HRESULT(STDMETHODCALLTYPE*LockServer)(IClassFactoryFAR*This,BOOLfLock);};

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

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

接口定义语言与类型库

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

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 ,以供其他语言编译器与运行时环境使用,如VB、Delphi、.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++类:

classSomeClass:publicISomeInterface{......};

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

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

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

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

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

引用计数

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

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

下述是如何调用COM对象的AddRef与Release的指引:

函数、方法返回接口的引用(通过返回值或者"out"参数),应当在返回前增加被返回的对象的引用计数。

接口指针被覆盖或超出作用域之前,必须调用接口指针的Release方法。

如果一个接口引用指针被复制,必须调用该指针的AddRef方法。

AddRef与Release必须在被引用的相关接口上调用。因为一个COM对象可能实现了逐个接口上的引用计数,使得仅在相关接口上内部分配资源。

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

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

其他能直接支持COM的库与语言还包括MFC Visual C++编译器的COM支持 、VBScript、Visual Basic、ECMAScript(JavaScript)和Borland Delphi等。

程序设计

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

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

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

使用注册表

在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) ) 或者存储在可执行文件的资源中或组件安装时的单独文件中。 这使得同一组件的不同版本可以安装在不同目录下,用其各自的manifest描述,直接 复制安装 ( 英语 : XCOPY deployment ) 。 这种技术有限支持EXE COM服务器 且不能用于系统范围组件如MDAC、MSXML、DirectX或Internet Explorer。

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

进程与网络透明

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

线程化与“套间”

在COM中,每个线程属于某个“套间”(apartment)。 套间是一个逻辑容器,收纳遵循相同线程访问规则的com对象与线程。套间本质上只是一个概念,没有句柄类型可以引用它,更没有可调用的API操纵它。COM有3类套间:单线程套间(Single Threading Apartment,STA),多线程套间(MTA),以及线程中立套间(Thread Neutral Apartment,NA)。每一种套间类型表示在一个进程内部是多线程情况下,如何同步对COM对象的调用。例如,一个进程加载了一个COM的DLL文件后,该DLL可能定义并使用了一些非常值的全局变量,该进程内的多个线程如何并发访问该DLL并保证是线程安全的,这就是套间技术需要解决的问题。进程可以包含多个COM对象,其中一些可以用STA,其它可以用MTA。所有线程以类似方式访问同一apartment中的COM对象。COM对象与线程的apartment的选择在运行时确定后就不能再改变。一个COM对象仅且只能存在于一个套间,COM对象一经创建就确定所属套间,并且直到销毁它一直存在于这个套间。一个COM线程(创建了COM对象或者调用了COM方法的线程)也属于一个套间,COM线程从创建到结束都存在于同一套间。一个进程最多只有一个多线程套间。跨套间的方法调用要通过列集实现,这就需要采用proxies和stubs。

线程必须通过调用CoInitializeEx()函数并且设定参数为COINIT_APARTMENTTHREADED或者COINIT_MULTITHREADED初始化COM运行时。访问了CoInitializeEx()函数的线程即已进入套间,直到线程调用CoUninitialize()函数或者自身终止,才会离开套间。

线程与属于同一套间的对象遵循相同的线程访问规则。可以直接执行方法调用而不需COM的辅助。跨套间边界的方法调用需要 安整(marshalling) ( 英语 : Marshalling (computer science) ) 。这需要代理(proxies)与“桩”(stubs)。

批评

消息泵

STA初始化时,创建一个隐藏窗口,用于apartment之间、进程间的消息路由。该窗口必须有正常的消息队列泵。这种结构称为消息泵。早期版本的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克服了这一问题。

参见

可移植对象 ( 英语 : Portable object (computing) ) ,跨语言跨平台对象模型定义

Distributed COM(DCOM),使COM能在网络中工作的扩展

通用语言架构,当前的.NET跨语言跨平台对象模型

Windows Runtime,应用程序模型,指向Windows 8的COM演进版本

CORBA,通用对象请求代理体系结构,开放式跨平台对象模型

D-Bus,开放跨语言跨平台对象模型

KDE Frameworks 5KDE组件框架

SOM ( 英语 : IBM System Object Model ) ,IBM系统对象模型,功能丰富的COM替代品

XPCOM,Mozilla应用程序跨平台组件对象模型

JavaBeans

Java远程方法调用

ICE (中间件)

绑扎

Foreign function interface ( 英语 : Foreign function interface )

调用约定

名字修饰

应用程序接口- API

应用二进制接口- ABI

SWIG ( 英语 : SWIG ) ,开源自动接口绑定生成器,从许多语言到其他语言

参考文献

COM: A Brief Introduction (powerpoint). [ March 7, 2006] .

Box, Don. Essential COM. Addison-Wesley. 1998. ISBN 0-201-63446-5.

Chappell, David. Understanding ActiveX and OLE. Microsoft Press. 1996. ISBN 978-1-57231-216-6.

Integration and Migration of COM+ services to WCF. [ April 15, 2010] .


免责声明:以上内容版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。感谢每一位辛勤著写的作者,感谢每一位的分享。

——— 没有了 ———
编辑:阿族小谱
发表评论
写好了,提交
{{item.label}}
{{commentTotal}}条评论
{{item.userName}}
发布时间:{{item.time}}
{{item.content}}
回复
举报
点击加载更多
打赏作者
“感谢您的打赏,我会更努力的创作”
— 请选择您要打赏的金额 —
{{item.label}}
{{item.label}}
打赏成功!
“感谢您的打赏,我会更努力的创作”
返回

更多文章

更多精彩文章
打赏
私信

推荐阅读

· 文档对象模型
标准化W3C对DOM进行标准化的动作,目前已经推行至第三代。Level1DocumentObjectModel(DOM)Level1SpecificationLevel2DocumentObjectModel(DOM)Level2CoreSpecificationDocumentObjectModel(DOM)Level2HTMLSpecificationDocumentObjectModel(DOM)Level2ViewsSpecificationDocumentObjectModel(DOM)Level2StyleSpecificationDocumentObjectModel(DOM)Level2EventsSpecificationDocumentObjectModel(DOM)Level2TraversalandRangeSpecificationLevel3DocumentOb...
· 软件组件
参见基于组件的开发
· 外设组件互连标准
历史一张PCI-X的千兆以太网卡PCI标准的开发于1990年前后开始于英特尔。1992年6月22日,英特尔发表PCI1.0标准,该标准仅限于组件级规范。1993年4月30日,PCI-SIG发表了PCI2.0标准,这个标准第一次创建了连接器与主板插槽间的标准。PCI标准被立即应用于服务器中,取代了先前的MCA及EISA,成为服务器扩展总线的不二选择。在主流个人计算机中,PCI标准则缓慢地取代着VESA局部总线(VLB);直至1994年后期第二代奔腾PC推出后,该标准方得以实现具有重要意义的市场突破。1996年,VLB彻底退出了个人计算机市场,制造厂商甚至将PCI标准应用于486计算机中。EISA标准则与PCI标准共存使用至2000年。苹果电脑在1995年中期将PCI标准应用于专业产品PowerMacintosh电脑中(而取代了NuBus),而消费者产品MacintoshPerforma则于1...
· 感光耦合组件
发展史CCD是于1969年由美国贝尔实验室的威拉德·博伊尔和乔治·史密斯所发明的。当时贝尔实验室正在发展视频电话和半导体气泡式内存。将这两种新技术结起来后,博伊尔和史密斯得出一种设备,他们命名为“电荷‘气泡’组件”(Charge"Bubble"Devices)。这种设备的特性就是它能沿着一片半导体的表面传递电荷,便尝试用来做为记忆设备,当时只能从寄存器用“注入”电荷的方式输入记忆。但随即发现光电效应能使此种组件表面产生电荷,而组成数字视频。1971年,贝尔实验室的研究员已能用简单的线性设备捕捉视频,CCD就此诞生。有几家公司接续此一发明,着手进行进一步的研究,包括飞兆半导体、美国无线电公司和德州仪器。其中飞兆半导体的产品率先上市,于1974年发表500单元的线性设备和100x100像素的平面设备。2006年元月,博伊尔和史密斯获颁电机电子工程师学会颁发的CharlesStarkDraper...
· 自由对象
自由函子范畴论为自由对象提供了普遍框架。考虑一种代数结构(如群、模等等)的范畴C{\displaystyle{\mathcal{C}}}。其上具有一个遗忘函子U:C→→-->Set{\displaystyleU:{\mathcal{C}}\to\mathbf{Set}},此函子将一个对象映至其下的集合;换言之,此函子“遗忘”所有代数操作。若U{\displaystyleU}有左伴随函子F:Set→→-->C{\displaystyleF:\mathbf{Set}\to{\mathcal{C}}},则称之为C{\displaystyle{\mathcal{C}}}的自由函子。F(X){\displaystyleF(X)}可以设想为由集合X{\displaystyleX}生成的自由对象,此时也有映射X→→-->F(X){\displaystyleX\toF(X)}(在此滥用了...

关于我们

关注族谱网 微信公众号,每日及时查看相关推荐,订阅互动等。

APP下载

下载族谱APP 微信公众号,每日及时查看
扫一扫添加客服微信