windows C++- Com技术简介(上)

在介绍C++和winrt与COM组件技术的关系之前,有必要介绍一下com组件技术,这项技术比较古老,但是它一直作为windows的基石存在。COM 是一类独立于平台且面向对象的分布式系统,用于创建可交互的二进制软件组件。 COM 技术是 Microsoft OLE(复合文档)和 ActiveX(支持 Internet 的组件)技术的基础。

注意我们仅仅介绍基础的COM组件技术,而不是全面的覆盖这项技术,讲述这项技术是一项非常困难的工作,所以许多知识点我们只是提及,不会深究,我个人比较推荐的书是《com组件论》,这本书非常值得一看。

COM技术被认为是C++进阶的高级技术,它从本质上解释了接口和实现,但是由于COM编程过于复杂,并且COM技术本质上windows使用的技术,所以慢慢的它被隐藏到系统深处了。

组件对象模型

Microsoft 组件对象模型 (COM) 是一个独立于平台的面向对象的分布式系统,可用于创建可以交互的二进制软件组件。 COM 技术是 Microsoft OLE(复合文档)和 ActiveX(支持 Internet 的组件)以及其他组件的基础技术。

要理解 COM(以及所有基于 COM 的技术),关键是要理解它不是一种面向对象的语言,而是一种标准。 COM 也没有指定应用程序的结构;语言、结构和实现细节留给应用程序开发人员。 相反,COM 指定对象模型和编程要求,使 COM 对象(也称为 COM 组件,有时简称为对象)能够与其他对象进行交互。 这些对象可以在单个进程中,也可以在其他进程中,甚至可以在远程计算机上。 它们可以以不同的语言编写,并且它们在结构上可能大相径庭,这就是为什么 COM 被称为二进制标准的原因;在程序转换为二进制计算机代码后应用的标准。

COM 唯一的语言要求是,代码是用一种可以创建指针结构并通过指针显式或隐式调用函数的语言生成的。 面向对象的语言(如 C++ 和 Smalltalk)提供简化 COM 对象的实现的编程机制,而 C、Java 和 VBScript 等语言可用于创建和使用 COM 对象。

COM 定义 COM 对象的基本性质。 通常,软件对象由一组数据和操作数据的函数组成。 COM 对象是专门通过一组或多组相关函数来访问对象数据的对象。 这些函数集被称为接口,接口的函数被称为方法。 此外,COM 要求访问接口方法的唯一方法是通过指向接口的指针。

除了指定基本的二进制对象标准外,COM 还定义了某些基本接口,这些接口提供了所有基于 COM 的技术所共有的功能,并提供了所有组件所需的少量功能。 COM 还定义了对象如何在分布式环境中协同工作,并添加了安全功能来帮助提供系统和组件完整性。

COM 简介

Microsoft 组件对象模型 (COM) 定义了二进制互操作性标准,可利用此标准创建在运行时交互的可重用软件库。 无需将 COM 库编译到应用程序中即可使用 COM 库。 COM 是许多 Microsoft 产品和技术的基础,例如 Windows Media Player 和 Windows Server。

COM 定义适用于许多操作系统和硬件平台的二进制标准。 对于网络计算,COM 定义了标准线路格式和协议,用于在不同硬件平台上运行的对象之间交互。 COM 独立于实现语言,这意味着可以使用不同的编程语言(如 C++ 和 .NET Framework 中的编程语言)创建 COM 库。

COM 规范提供了启用跨平台软件重用的所有基本概念:

  • 组件之间的函数调用的二进制标准;
  • 用于将函数强类型分组到接口中的预配;
  • 提供多态性、功能发现和对象生存期跟踪的基接口;
  • 唯一标识组件及其接口的机制;
  • 从部署创建组件实例的组件加载程序;

COM 有许多部件协同工作,从而支持创建从可重用组件生成的应用程序:

  • 提供符合 COM 规范的运行时环境的主机系统;
  • 定义功能协定的接口和实现接口的组件;
  • 向系统提供组件的服务器和使用组件提供的功能的客户端;
  • 用于跟踪组件在本地和远程主机上的部署位置的注册表;
  • 用于找到本地和远程主机上的组件并将服务器连接到客户端的服务控制管理器;
  • 定义如何在主机文件系统上导航文件内容的结构化存储协议;

启用跨主机和平台的代码重用是 COM 的核心。 可重用接口实现命名为组件、组件对象或 COM 对象。 一个组件实现一个或多个 COM 接口。

通过设计库实现的接口,可定义自定义 COM 库。 库的使用者无需了解库的部署和实现详细信息即可发现和使用其功能。

对象和接口

COM 对象通过接口公开其功能,接口是成员函数的集合。 COM 接口定义组件的预期行为和职责,并指定一个强类型协定,该协定提供一小组相关操作。 COM 组件之间的所有通信都通过接口进行,并且通过组件接口公开组件提供的所有服务。 调用方只能访问接口成员函数。 除非在接口中公开内部状态,否则调用方无法使用内部状态。

接口是强类型。 每个接口都有自己的唯一接口标识符(名为 IID),这样就消除了与人类可读名称可能发生的冲突。 IID 是全局唯一标识符 (GUID),此标识符与开放软件基金会 (OSF) 分布式计算环境 (DCE) 定义的通用唯一 ID (UUID) 相同。 创建新接口时,必须为该接口创建新的标识符。 当调用方使用接口时,它必须使用唯一标识符。 此显式标识可消除会导致运行时失败的命名冲突,从而提高稳健性。

定义新接口时,可以使用接口定义语言 (IDL) 创建接口定义。 从此接口定义中,Microsoft IDL 编译器会生成标头文件供使用接口的应用程序使用,并生成源代码来处理远程过程调用。 Microsoft 提供的 IDL 基于 DCE IDL 的简单扩展,这是基于远程过程调用 (RPC) 的分布式计算的行业标准。 IDL 是可方便接口设计人员的工具,不是 COM 互操作性的核心。 使用 IDL 时,无需为每个编程环境手动创建标头文件。

在 COM 接口中很少使用继承。 COM 仅支持接口继承以重用与基接口关联的协定。 COM 不支持选择性继承;因此,如果一个接口继承自另一个接口,则它包括基接口定义的所有函数。 此外,接口仅使用单重继承而不是多重继承从基接口获取函数。

接口实现

不能单独创建 COM 接口的实例。 而是创建实现接口的类实例。 在 C++ 中,COM 接口建模为抽象基类,这意味着接口是仅包含纯虚拟成员函数的 C++ 类。 C++ 库会从一个或多个接口继承成员函数签名、覆盖每个成员函数并为每个函数提供实现,从而实现 COM 对象。

可以使用任何支持函数指针概念的编程语言来实现 COM 接口。 例如,在 C 中,接口是一个结构,其中包含指向函数指针表的指针,此结构用于接口中的每个方法。

实现接口时,类必须为接口中的每个函数提供实现。 如果类在接口函数中无事可做,则实现可以是单个 return 语句。

COM 类可使用唯一的 128 位类 ID (CLSID) 标识,该 ID 将类与文件系统中的特定部署相关联,对于 Windows 而言这是 DLL 或 EXE。 CLSID 是 GUID,这意味着没有其他类具有相同的 CLSID。 使用唯一类标识符可防止类之间发生名称冲突。 例如,两个不同的供应商可以编写名为 CStack 的类,但两个类都具有唯一的 CLSID,因此可避免出现任何冲突。

可以使用 CoCreateGuid 函数或使用在内部调用此函数的 COM 创作工具(如 Visual Studio)获取新的 CLSID。

IUnknown 接口

所有 COM 接口都继承自 IUnknown 接口。 IUnknown 接口包含基本 COM 操作,可实现多态性和用于实例生存期管理。 IUnknown 接口有三个成员函数,名为 QueryInterface、AddRef 和 Release。 要实现 IUnknown 接口,需要所有 COM 对象。

QueryInterface 成员函数为 COM 提供多态性。 调用 QueryInterface 以在运行时确定 COM 对象是否支持特定接口。 如果 COM 对象实现请求的接口,则返回 ppvObject 参数中的接口指针,否则返回 NULL。 QueryInterface 成员函数支持在 COM 对象支持的所有接口之间导航。

COM 对象实例的生存期由其引用计数控制。 IUnknown 成员函数 AddRef 和 Release 控制计数。 AddRef 会递增计数,而 Release 会递减计数。 当引用计数达到零时,Release 成员函数可能会释放实例,因为没有调用方在使用它。

客户端/服务器模型

一个 COM 类可实现许多 COM 接口。 实现由调用方与 COM 类实例交互时运行的二进制文件组成。 COM 支持在不同应用程序中使用类,包括在不了解特定类的情况下编写的应用程序。 在 Windows 平台上,类存在于动态链接库 (DLL) 或其他应用程序 (EXE) 中。

在其主机系统上,COM 维护系统上安装的 COM 对象的所有 CLSID 的注册数据库。 注册数据库是每个 CLSID 与存放相应类的 DLL 或 EXE 的位置之间的映射。 只要调用方想要创建 COM 类的实例,COM 就会查询此数据库。 调用方只需知道 CLSID 即可请求类的新实例。

COM 对象与其调用方之间的交互建模为客户端/服务器关系。 客户端是从系统请求 COM 对象的调用方,而服务器是存放向客户端提供服务的 COM 对象的模块。

COM 客户端是将 CLSID 传递给系统以请求 COM 对象实例的任何调用方。 创建实例的最简单方法是调用 COM 函数 CoCreateInstance。

CoCreateInstance 函数会创建指定 CLSID 的一个实例,并返回客户端所请求类型的接口指针。 客户端负责管理实例的生存期,方法是在客户端使用完实例后调用其 Release 函数。 若要根据单个 CLSID 创建多个对象,请调用 CoGetClassObject 函数。 若要连接到已创建且正在运行的对象,请调用 GetActiveObject 函数。

COM 服务器向系统提供 COM 实现。 服务器将 CLSID 与 COM 类相关联,存放类的实现,实现用于创建类实例的类工厂,并可用于卸载服务器。

COM 服务器与提供给系统的 COM 对象不同。

若要启用创建 COM 对象,COM 服务器必须提供 IClassFactory 接口的实现。 客户端可以调用 CreateInstance 方法,以请求 COM 对象的新实例,但通常将此类请求封装在 CoCreateInstance 函数中。

可以将 COM 服务器部署为在运行时加载到客户端进程中的共享库(Windows 平台上的 DLL),或部署为可执行模块(Windows 平台上的 EXE)。

相关推荐
可均可可27 分钟前
C++之OpenCV入门到提高004:Mat 对象的使用
c++·opencv·mat·imread·imwrite
白子寰1 小时前
【C++打怪之路Lv14】- “多态“篇
开发语言·c++
小芒果_011 小时前
P11229 [CSP-J 2024] 小木棍
c++·算法·信息学奥赛
gkdpjj1 小时前
C++优选算法十 哈希表
c++·算法·散列表
王俊山IT1 小时前
C++学习笔记----10、模块、头文件及各种主题(一)---- 模块(5)
开发语言·c++·笔记·学习
-Even-1 小时前
【第六章】分支语句和逻辑运算符
c++·c++ primer plus
我是谁??2 小时前
C/C++使用AddressSanitizer检测内存错误
c语言·c++
发霉的闲鱼2 小时前
MFC 重写了listControl类(类名为A),并把双击事件的处理函数定义在A中,主窗口如何接收表格是否被双击
c++·mfc
小c君tt2 小时前
MFC中Excel的导入以及使用步骤
c++·excel·mfc
xiaoxiao涛2 小时前
协程6 --- HOOK
c++·协程