欢迎光临散文网 会员登陆 & 注册

组件对象模型 1:概述

2022-05-04 16:12 作者:CSDN首席喷子  | 我要投稿

组件对象模型(Component Object Model)是微软的组件技术,能够创建二进制可复用的程序。其问题来源于代码的复用机制,最早的复用当然是源代码级别的复用,这种方法的问题在于:

  1. 必须公开源代码。

  2. 代码会在所有复用的地方占据内存,假如在100个进程复用,就要重复占用100次。

后来出现了动态链接库技术,解决了公开代码和重复内存占用的问题,但是DLL仍然不是一个“可复用组件”,因为:

  1. 由于C++标准没有规定ABI,因此不同的编译器编译出来的C++程序必然不兼容,甚至同一厂商编译器的不同版本都不能兼容。

  2. 即使C++编译器兼容了,DLL依然需要一个C++编译器才能使用,如果是VB就不能执行。

COM能够解决ABI的差异性,又能创建不同语言之间的“胶水”,其思路如下:
  1. 不同C++编译器在C部分的ABI必然是互相兼容的,导致C++ ABI不兼容的主要原因是虚函数vtbl的处理不一致。既然如此,可以利用抽象类创建接口,宿主程序只负责调用抽象虚函数,具体执行交给DLL。

  2. 将胶水代码的调用规则内置在系统中,并创建一个中心化的服务进程(scm),组件登记到注册表,并记录其调用方法。

1  COM标准

COM组件是由一组接口(C++抽象类,指明调用规范)和一组实现类组成的,其基础接口是IUnknown,实例如下:

可以看到:

  • 任何接口都必须继承自IUnknwon。

  • 所有的接口调用传参规则都是__stdcall,并返回一个用于表示操作状态的HRESULT。

  • 接口所有的函数都必须是虚函数。

  • COM组件必须手动管理内存,AddRef函数用于引用自增1,返回新的引用数,引用数应该是一个32位无符号整数。

  • Release函数用于引用自减1,返回新的引用数。

  • QueryInterface函数用于将IUnknwon变成需要的接口,第一个参数是接口ID,第二个参数是所需接口的指针。由于查询必然伴随一个新的引用,因此在此处需要调用AddRef。

2  编程实例

2.1  定义接口和实现类ID

下面以最简单的动态链接库COM组件为例进行说明。接口和实现类是根据一个128位UUID标记的,分别改名为IID和CLSID。首先定义接口和实现类的ID:

可以到Windows SDK的安装目录找到创建IID和CLSID的工具guidgen.exe。

2.2  定义接口和实现类

定义接口和实现类:

2.3  定义类工厂

注意,由于年代问题,类工厂在官方文档中被称为“类对象”(ClassObject),非常迷惑。之所以创建类工厂是因为Windows并不知道怎么创建实现类的对象,因此需要提供一个IClassFactory的实现:

2.4  导出系统调用函数

一个COM组件至少需要C语言命名模式导出3个函数:

  • DllCanUnloadNow

  • DllMain

  • DllGetClassObject

这三个函数都不需要用户手动调用,而是由系统在创建组件实例的时候调用的,其作用分别为:

(1)应用程序结束时判断是否能够卸载DLL。

(2)加载DLL时的事件处理函数。

(3)创建类工厂。

下面仅描述DllGetClassObject,其函数签名为:

一个实例实现如下:

可以看到其作用就是创建一个类工厂,然后调用其QueryInterface。不难猜到系统在这里调用QueryInterface的目的是获取一个IClassFactory指针。另外两个函数不需做其它工作:

为了导出函数还必须创建定义文件,内容如下:

编译,输出一个MyCOM.DLL文件。

2.5  导入注册表

至此,系统仍然不知道到哪里寻找这个组件。需以管理员权限到注册表的

HKEY_CLASSES_ROOT\CLSID

下面建立一个名为{CLSID}的项,并创建一个名为InProcServer32的子项。InProcServer32的默认值就是DLL文件的路径,如下图所示:

将组件导入注册表

3  客户端调用

客户端实例代码如下:

main.h

main.cpp


组件对象模型 1:概述的评论 (共 条)

分享到微博请遵守国家法律