再谈接口与实现
用户可以选择任何一个C++编译器,但最终他们必须要使用一个C++(而不是其它语言)的编译器.
COM提供了这样一种语言,它只用了大家都很熟悉的C语言的语法,同时加入了某些用于消除C语言中二义性的能力,称接口定义语言
IDL (Interface Definition Language)
COM IDL 以OSF的DCE RPC IDL 为基础加入与COM相关的扩展(继承,多态)
MIDL.EXE是IDL编译器,
产生C/C++兼容的头文件
产生一个二进制文件,称类型库文件
方法与效果
每个COM方法返回一个HRESULT值,
分成三部分:严重程度位,操作码和信息码
如HRESULT指示一个不正常的结果,则Java虚拟机会抛出异常,
而C++则要手工检查HRESULT.
接口与IDL
IDL用interface 关键字定义接口
定义:接口名,基接口名,接口体,接口属性
每个COM接口都至少有两个属性:一个是[object] 表明是COM 而不是DCE风格的接口; 一个是接口的实际名字[uuid]
GUID是一个128位的大数,可以保证在时间和空间范围内都是唯一的,标准文本格式用于[uuid]
IUnknown
方法: QueryInterface(), AddRef(), Release();
IUnknown是所有接口的根源
COM禁止多重接口继承,但实现可以暴露多个接口
资源管理与IUnknown
COM的引用计数规则:
1.当一个非空接口指针从一个内存位置拷贝到另一个内存位置时,调用AddRef();
2. 对已经包含非空接口指针的内存位置而言,在复写内存之前要调用Release();
3.如果可能,多余的AddRef()和Release()可以被优化掉;
调用规则:
AddRef()
A1 当把一个非空接口指针写到局部变量时;
A2 当被调用方把一个非空接口指针写到方法或者函数的[out]或[in, out]参数中时
A3 当被调用方返回一个非空接口指针作为函数的实际结果时
A4 把一个非空接口指针写到对象的一个数据成员时
Release ()
R1 改写一个非空局部变量或者数据成员之前
R2 离开非空局部变量的作用域之前
R3 被调用方要改写方法或者函数的[in, out]参数,且参数初始值非空时
R4 改写对象非空数据成员之前
R5 离开对象析构函数之前
特殊规则
S1 调用方把一个非空接口指针通过[in]参数传给函数时,不必调用A或R
类型强制转换与IUnknown
HRESULT QueryInterface([in] REFIID riid, [out] void **ppv);
若对象不支持所请求的接口类型,那么ppv置为null, 返回E_NOINTERFACE
若支持,则改写指针, 返回S_OK,调用AddRef();
AddRef 和Release不是针对整个对象的操作,而是针对接口指针的操作
Void**导致的类型不安全性漏洞,可用宏IID_PPV_ARG解决
实现IUnknown
AddRef()和Release()
类定义中的析构函数为protected,保证对象总是在堆上创建,delete this总是正确的.
但有时我们希望对象不是在堆中分配,这时引用计数可进行优化,返回一个参考值
QueryInterface()
当一个请求被多个接口支持时,类型转换操作必须明确选择一个更为精确的基类
使用COM接口指针
因为COM对应的C++语言映射没有提供runtime layer, C++程序员必须显示地使用IUnknown的方法,但这样代码效率更高
对Java 和VB而言, IUnknown 的细节隐藏在虚拟机之后.
智能指针可以使在C++中使用COM接口指针的操作简化.
但它也带来许多问题.
优化QueryInterface
每个COM兼容的类都提供了一个表格,通过固定的偏移或其它技术,把该类所支持的IID映射到对象的某个地方.
(类似于MFC中的消息映射宏)
数据类型
OLECHAR到TCHAR的转换
BSTR:字符串类型
IDL和COM也支持联合(Union),为保证解释不存在二义性,要提供全个鉴别器
COM提供了一个通用的,通过鉴别的联合给VB使用,称VARIANT
COM的接口也可以作为参数进行传递: 静态或动态
IDL属性与COM属性
指明一个对象具有某些公开可见的属性,并可通过COM接口来访问或修改这些属性天有些情况下是很有用的.
COM IDL允许接口的方法上加上一些注释,反映该方法可用于读写对象的某个属性
异常
COM的异常API: 抛出SetErrorInfo() 捕捉异常GetErrorInfo();
COM异常对象必须支持IErrorInfo接口
抛出异常的对象必须实现IsupportErrorInfo接口,以便确定哪个接口支持异常
COM要求禁止"纯C++异常"传播到方法的边界之外