COM 编程基础
DirectX 采用了 COM 标准。而 DirectShow 是一套完全基于 COM 的应用系统。要想深入学习 DirectShow,掌握一些 COM 编程的基础知识是必不可少的。
一、COM 是什么
COM(Component Object Model,组件对象模型)是微软公司于 1993 年提出的一种组件技术,它是一种平台无关、语言中立、位置透明、支持网络的中间件技术。
在 COM 架构下,人们可以开发出各种各样功能专一的 COM 组件,然后将它们按照需要组合起来,构成复杂的应用系统。由此带来的好处是多方面的:可以将系统中的组件用新的替换掉,以便随时进行系统的升级和定制;可以在多个应用系统中重复利用同一个组件;可以方便的将应用系统扩展到网络环境下。
COM 与语言、平台无关的特性使所有的程序员均可充分发挥自己的才智与专长编写组件模块。组件实际上是一些小的二进制可执行程序,它们可以给应用程序,操作系统以及其他组件提供服务。开发自定义的 COM 组件就如同开发动态的,面向对象的 API。多个 COM 对象可以连接起来形成应用程序或组件系统,并且组件可以在运行时刻,在不被重新链接或编译应用程序的情况下被卸下或替换掉。Microsoft 的许多技术,如 ActiveX, DirectX 以及 OLE 等都是基于 COM 而建立起来的。并且 Microsoft 的开发人员也大量使用 COM 组件来定制他们的应用程序及操作系统。
COM 所含的概念并不止是在 Windows 操作系统下才有效。COM 并不是一个大的 API,它实际上象结构化编程及面向对象编程方法那样,也是一种编程方法。在任何一种操作系统中,开发人员均可以遵循 “COM方法”。
二、COM 组件
COM 本身只是一种规范,而不是实现。当使用 C++ 来实现时,COM 组件就是一个 C++ 类,而接口都是纯虚类。可以用如下的 C++ 代码来简单描述一个 COM 组件。
class Ifunction
{
public:
virtual Method1(...) = 0;
virtual Method2(...) = 0;
//...
};
class MyObject : public Ifunction
{
public:
virtual Method1(...){...}
virtual Method2(...){...}
//...
};
其中,Ifunction 就是我们常说的接口,而 MyObject 就是 COM 组件。COM 规范规定,任何组件或接口都必须从 IUnknown 接口中继承而来。
COM 组件有 3 种类型:进程内组件、本地进程组件和远程组件。Filter 一般是一种进程内组件,以 DLL(动态链接库)的形式提供服务。
三、COM 基本元素
(1)接口:一个名字以大写的I
开头的抽象基类,包含一组虚方法,接口可以从其它接口继承,但不能继承于多个接口,只能从一个接口继承。
(2)coclass(Component Object Class,组件对象类,通常被称为 COM 类):COM 类通常就是一个 C++ 类,这个类继承自一个或者多个接口,并实现它们,COM 对象在内存中的表现就是这个 COM 类的一个实例。
(3)COM 库:操作系统的一部分,调用 COM 组件时就是 COM 库在协助你完成调用。
(4)COM 服务器:包含了一个或者多个 coclass 的二进制 DLLs 或者 EXE 执行体。
(5)注册:创建注册表入口的一个过程,告诉 Windows 操作系统 COM 服务器放在什么位置。
(6)取消注册:从注册表删除这些注册入口。
(7)GUID(全球唯一标示符 Globally unique identifier):是一个 128 位的数字,其实它和 COM 无关,在其他地方我们也经常看到它,只不过 COM 中的接口和 coclass 都拥有一个 GUID,因为是全球唯一的,所以避免了名称冲突。
(8)IID(接口ID):是 interface 的 GUID。
(9)CLSID(类 ID):是 coclass 的 GUID。
(10)HRESULT:是一个代表着成功或错误代码的整型或长整型的数字,对 COM 对象的调用经常会返回一个 HRESULT,虽然用H
开头,但并不是句柄的意思。
四、COM 与 DirectShow
DirectShow 应用程序实际上是一种 COM 组件的客户程序,只是 COM 组件的 “使用” 问题。这些问题包括如何创建 COM 组件、如何得到组件对象上的接口以及调用接口方法、如何管理组件对象(即需要熟悉 COM 的引用计数机制)等。下面的代码是最一般的步骤。
CoInitialize(NULL); // COM库初始化
// Do something
//...
IUnknown *pUnk = NULL;
IObject *pObject = NULL;
// 创建组件对象
HRESULT hr = CoCreateInstance(CLSID_Object, CLSCTX_INPROC_SERVER, NULL, IID_IUnknown,(void**)&pUnk);
if (SUCCEEDED(hr))
{
// 查询得到组件对象上的接口
hr = pUnk->QueryInterface(IID_IObject, (voidI**)&pObject);
if (SUCCEEDED(hr))
{
// 调用接口的方法
pObject->SomeMethod();
pObject->Release();
}
pUnk->Release();
}
//...
CoUninitialize(); // 释放COM库使用的资源
对于 Filter 开发人员来说,需要掌握的 COM 知识就要多一点。因为 Filter 本身是一种 COM 组件,开发 Filter 涉及到了 COM 组件的 “实现” 问题。而这里只对 COM 做简单介绍,想要进一步了解可以搜索《COM技术内幕》一书。
参考:
《DirectShow开发指南》1.3节 - COM 编程基础