《COM本质论》读书笔记
前言
虽然一直都不喜欢Windows的应用开发,不过由于现在工作需要,必须对 Win32 开发非常熟悉。 Windows 上的 C++ 开发,逃不过对组建对象模型COM(Component Object Model)编程的学习和理解,尤其是游戏、音视频领域 —— Direct3D/Direct2D/DirectShow/DirectSound 等等。COM已经是上个世纪的产物了,所以相关的学习资料也都很老了,不过《COM本质论》是一本不错的入门书。在此我做做笔记加深理解。
第1章:COM是一个更好的C++
"静态库",不同程序有相同的代码片段 FastString.obj。
“动态库”,不同程序可以指向相同的代码片段,减少内存空间。
第2章:接口
void Method1([in] long arg1, [out] long *parg2, [in,out] long parg3);
#define SUCCEEDED(hr) (long(hr) >= 0) #define FAILED(hr) (long(hr) < 0)
[attribute1, attribute2, ...] interface IThisInterface: IBaseInterface { typedef1; typedef2; ... method1; method2; ... }
下面是一个Interface的例子(一个计算器的例子):
[object, uuid(BDA4A270-A1BA-11d0-8C2C-0080C73925BA)] interface ICalculator : IBaseInterface { HRESULT Clear(void); HRESULT Add([in] long n); HRESULT Sum([out, retval] long *pn); }
其中, [object] 和 [uuid] 都是 attribute,并且每个COM接口都必须要有 [object] 属性,说明该接口定义是一个 COM 接口。
下面是 COM 所有 interface 的基类 —— IUnknown 的 C++ 定义(其中 interface 就是 struct):
extern "C" const IId IID_IUnknown; interface IUnknown { virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) = 0; virtual ULONG STDMETHODCALLTYPE AddRef(void) = 0; virtual ULONG STDMETHODCALLTYPE Release(void) = 0; };
IDL语言中,为了从一个接口派生另一个接口,我们要么在同一个文件中定义基接口,要么使用 import 指示符:
// calculator.idl [object, uuid(BDA4A270-A1BA-11d0-8C2C-0080C73925BA)] interface ICalculator : IUnknown { import "unknwn.idl"; // 引入 IUnknown 的定义 HRESULT Clear(void); HRESULT Add([in] long n); HRESULT Sum([out, retval] long *pn); }
另外,COM不支持多继承语法。
COM 的 IUnknown接口的 “运行时类型发现”就是 QueryInterface 方法,它的 IDL 描述:
HRESULT QueryInterface([in] REFIID riid,
[out] void **ppv);
第一个参数 riid 是被请求的接口的实质名字,第二个参数 ppv 是指向第一个接口指针变量。
C++ 程序员必须显示地使用 IUnknown 的方法,因为 COM 所对应的 C++ 语言映射并没有在客户代码和对象代码之间提供一个运行时层(runtime layer)。不过这样运行效率更高。
第3章:类
三个不同的概念: interface, implementation, class
· interface: 与对象通信的抽象协议
· implementation: 支持一个或多个接口的具体数据类型
· class: 被命名的 implementation,代表了具体的、可实例化的类型,
interface IMoniker : IPersistStream { HRESULT BindToObject([in] IBindCtx *pbc,
[in, unique] IMoniker *pmkToLeft,
[in] REFIID riid,
[out, iid_is(riid)] void **ppv); }
第4章:对象