windows

文章目录 COM 简介概念CoCreateInstanceIUnKnown 接口IUnknown 的工作原理引用计数接口查询

COM 简介

Microsoft 组件对象模型 (COM) 是一个独立于平台的分布式面向对象的系统,用于创建可交互的二进制软件组件。 COM 是 Microsoft 的 OLE (复合文档) 、ActiveX (Internet 组件) 等的基础技术。 若要了解 COM (所有基于 COM 的技术) ,必须了解它不是面向对象的语言,而是一种标准。 用于创建在运行时进行交互的可重用的软件库。 无需将其编译到应用程序中,即可使用 COM 库。 COM 是许多 Microsoft 产品和技术的基础,例如 Windows Media Player 和 Windows 服务器。

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

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

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

COM 具有多个共同工作的部分,可让你创建从可重用组件生成的应用程序:

提供符合 COM 规范的运行时环境的 主机系统 。定义功能协定和实现接口的 组件 的 接口。向系统提供组件的 服务器,以及使用组件提供的功能的 客户端。跟踪将组件部署在本地和远程主机上的位置的 注册表 。一种 服务控制管理器 ,用于查找本地和远程主机上的组件,并将服务器连接到客户端。一种 结构化存储 协议,用于定义如何在主机的文件系统上导航文件的内容。 跨主机和平台启用代码重用是 COM 的核心。 可重复使用的接口实现称为 " 组件"、" 组件对象" 或 " COM 对象"。 组件实现一个或多个 COM 接口。

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

概念

接口:成员函数的集合,面向对象中接口类。 IID:每个接口都有其自己的唯一接口标识符 IID, IID 是全局唯一标识符 (GUID) , IID只会对应一个接口

COM类: COM类可以创建实现了多个 接口(接口类) CLSID:每个com 类都有一个CLSID,在windows 上com 类 的实现在 dll 或者 exe 上。

映射关系:COM 维护系统上安装的 COM 对象的所有 Clsid 的注册数据库。 注册数据库是每个 CLSID 之间的映射,以及承载相应类的 DLL 或 EXE 的位置。 每当调用方想要创建 COM 类的实例时,COM 都将查询此数据库。 调用方只需了解用于请求类的新实例的 CLSID。

客户端/服务器:COM 对象及其调用方之间的交互建模为客户端/服务器关系。 客户端是向系统请求 COM 对象的调用方,服务器是包含向客户端提供服务的 COM 对象的模块。

CoCreateInstance

com 框架提供的接口,用于获取想要的对象指针 COM 客户端是向系统传递 CLSID 以请求 COM 对象实例的任何调用方。 创建实例的最简单方法就是调用 COM 函数 CoCreateInstance。

这是一段dshow 创建graph 的代码

ComPtr graph;HRESULT hr;hr = CoCreateInstance(CLSID_FilterGraph, NULL, CLSCTX_INPROC_SERVER, IID_IFilterGraph, (void **)&graph);

看一下

HRESULT _stdcall CoCreateInstance(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv);

第一个参数:待创建组件的CLSID。 用于找到对应COM 类,和所在的DLL 或者 exe。

第二个参数:用于聚合组件。

第三个参数:dwClsContext的作用是限定所创建的组件的执行上下文。

第四个参数:iid为组件上待使用的接口的iid。也就是 接口类对应的IID

CoCreateInstance 将在最后一个参数中返回此接口的指针。通过将一个IID传给CoCreateInstance,客户将无需在创建组件之后去调用 其QueryInterface函数。

HRESULT CoCreateInstance(const CLSID& clsid, IUnknown* punkonwnDuter, DWORD dwClsContext, const IID& iid, void** ppv){ // Set the out paameter to NULL *ppv = NULL; // 首先根据clsid获得COM类的工厂类 IClassFactory* pIFactory = NULL; HRESULT hr = CoGetClassObject(clsid, dwClsContext, NULL, IID_IClassFactory, (void**)&pIFactory); if (SUCCEEDED(hr)) { // 使用工厂类,传入接口 IID, 来创建接口对象 hr = pIFactory->CreateInstance(punkonwnDuter, iid, ppv); pIFactory->Release()(); } return hr;} IUnKnown 接口

IUnknown是指COM标准接口。所有COM接口都继承IUnKnown。 他负责两项工作:

IUnknown::QueryInterface负责得到该组件的其他接口的指针IUnknown::AddRef/Release负责管理该组件的生存期,但有人使用该组件时,保证该组件不会被意外删除;再没人使用该组件时,保证该组件被自动删除 IUnknown 的工作原理

IUnknown 中的方法使应用程序能够查询组件上的接口并管理组件的引用计数。

引用计数

引用计数是一个内部变量,在 AddRef 方法中递增,并在 Release 方法中减少。 基类管理引用计数,并同步对多个线程中的引用计数的访问。

接口查询

查询接口也很简单。 调用方传递两个参数:接口标识符 (IID) ,以及指针的地址。 如果组件支持请求的接口,它会将指针设置为接口,并递增其自己的引用计数,并返回 S _ OK。 否则,它会将指针设置为 NULL ,并返回 E _ NOINTERFACE。 以下伪代码显示了 QueryInterface 方法的一般轮廓。 下一节中描述的组件聚合带来了一些额外的复杂性。

第一点:说简单一点就是,dynamic_cast。查询 CoCreateInstance 创建出来的对象,是否可以转换成底层接口,其实就是c++ 中 dynamic_cast,同时增加一个引用计数而已。如果是纯c++ 的情况,应该就不需要这个接口了。

第二点:也是工厂模式,根据riid 创建 对象

STDMETHODIMP CapturePin::QueryInterface(REFIID riid, void **ppv){if (riid == IID_IUnknown) {AddRef();*ppv = this;} else if (riid == IID_IPin) {AddRef();*ppv = (IPin *)this;} else if (riid == IID_IMemInputPin) {AddRef();*ppv = (IMemInputPin *)this;} else {*ppv = nullptr;return E_NOINTERFACE;}return NOERROR;}

比丘资源网 » windows

发表回复

提供最优质的资源集合

立即查看 了解详情