写在前面:我没有开发过COM组件的经验,只是在做文献综述的时候需要了解这方面的知识,所以如果哪里说错了或者是我理解错了,还希望大家可以指出,谢谢。我不是程序员也不是计算机专业,只是课题偏了这个方向,因此很多基础概念或许有理解错,真心希望可以得到大家的指正,再次感谢。
COM object以接口的方式向客户端提供服务。一个COM component可以包含多个COM object,一个COM object可以有多个接口,其关系如图1所示。
图1 COM组件、COM对象和接口关系
在Windows OS上,COM组件以.dll或者.exe的文件形式存在。其中.dll是进程内组件(in-process component),.exe是进程外组件(out-process component)。进程内组件意味着该组件会被加载到客户端所在的进程中,而进程外组件则意味着该组件会被加载到一个新的进程中。
二进制接口是COM组件中的重要概念,正是因为存在binary interface compatibility, 组件的开发语言不受限制,不一定非得是C/C++。接口是用Microsoft Interface Definition Language (MIDL)描述的。每个接口有唯一标识,使用IID(interface identifier)命名。接口都需要继承自IUnknown接口,该接口提供了对接口的生存期控制和接口查询。下面是一个用MIDL描述的接口的例子:
interface IWithdraw : IUnknown { HRESULT Withdraw([in] Bank bank, [in] Integer amount, [out, retval] int* balance); };
每个COM object也有一个唯一标识,用CLSID表示。客户端通过COM库和类厂成功创建了对象的实例后,会得到一个指向COM object某个接口的指针(pointer),该指针实际上又指向了另一个指针(interface pointer),第二个指针指向一组函数,称为接口函数表或虚函数表(virtual table pointer),如图2所示。
图2 接口结构
COM component使用前需要先注册。组件程序把它所实现的COM object的信息以及接口信息都保存在注册表中,然后COM库通过系统注册表所提供的信息进行组件的创建工作。
在组件的创建工作中,还需要用到类厂(class factory)。类厂本身也是一个COM object,它支持一个特殊的接口IClassFactory,其定义如下:
class IClassFactory : public IUnknown { virtual HRESULT_stdcall CreateInstance(IUnknown *pUnknownOuter, const IID&iid, void**ppv) = 0; virtual HRESULT_stdcall LockServer(BOOL bLock) = 0; };
其中,CreateInstance用于创建对应的COM对象。那么,客户端创建COM对象的过程就应该是:
- 客户端调用COM库中CoCreateInstance函数或者CoGetClassObject函数
- COM库从注册表中找到相应的DLL程序并载入到进程中
- 调用组件程序中DllGetClassObject函数,创建类厂,并把类厂接口指针返回给CoGetClassObject函数或者CoCreateInstance函数
- 然后创建类厂对象,进而类厂创建COM对象,类厂把COM对象返回给CoGetClassObject函数或者CoCreateInstance函数,最后客户端可以直接调用COM对象。
COM组件支持两种方式的重用,分别是包容(containment/delegation)和聚合(aggregation),如图3所示。
a) containment b) aggregation
图3 COM组件的两种重用方法
Containment。对象A包含在对象B中,客户端想要调用对象A中的方法时,需要通过对象B来调用。
Aggregation。对象A的接口暴露在对象B的外面。当客户端想要调用对象A中的方法时,直接调用。但是客户端并不知道对象A的存在。
.NET中使用的是程序集(assembly)这个概念。使用C#编写的程序编译后成为程序集(.dll或者.exe格式的文件)。程序集的组成如图4所示。
图4 程序集的组成
其中,程序集列表是必须有的(assembly manifest)。程序集列表包括该程序集元数据,例如与指定程序集版本要求、安全标识所需的所有元数据,以及定义程序集范围和解析对资源和类的引用所需的所有元数据。
程序集列表中的内容包括:
assembly name、version number、culture、strong name information (这四项组成了程序集的标识)、list of all files in the assembly、type reference information、information on referenced assemblies
https://docs.microsoft.com/en-us/dotnet/framework/app-domains/assembly-manifest
程序集中无需使用IDL文件来定义组件接口的信息,取而代之的是元数据(metadata)。元数据包括程序集的一切基本信息,比如版本、类型、命名空间、依赖的其他程序集信息等。一个.dll文件中的元数据信息可以通过reflection获取(包括命名空间、类、属性、方法等)。编译器会自动地把程序中的这些相关信息封装在程序集的元数据中。因此,可以说程序集是一种自描述的组件。元数据的使用也使得程序集无需像COM组件,在使用前需要注册。详情可见:
https://docs.microsoft.com/en-us/dotnet/standard/metadata-and-self-describing-components
MSIL code是指程序集会首先被编译成一种微软中间语言(Microsoft Intermediate Language),程序无论是用C#写还是VB写,编译成IL都是等效的。.NET平台是建立在CLR(Common Language Runtime)基础上的,也就是说用C#写的程序要经历过两次译码才能在机器上运行,这与C++是不一样的。第一次译码是转换成IL,第二次才是转换成与操作系统相匹配的二进制指令。第二次是CLR负责的。这样运行在CLR基础上的被称为托管代码,而COM组件则是用非托管代码实现的。
resources就是指程序集包含的一些可用资源,这些资源在程序集运行时是可以使用的,包括字符串、图片以及特殊文件等。
因此,.NET组件(assembly)与COM组件相比具有自描述、自包含的特点。其在使用时无需注册,在创建时也无需使用COM库和类工厂,接口定义和实现也不是分开的。
COM组件的二进制兼容性是通过使用接口指针和虚函数表实现的,而.NET组件的二进制兼容性是通过使用元数据实现的。
来源:https://www.cnblogs.com/larissa-0464/p/11095203.html