1. COM对象至少需要实现一个接口。 而客户得到一个接口,根据COM规范,也可以通过QueryInterface方法得到其他所有的接口(IUnknown存在的理由之一?)
2. 包容与聚合的区别?
包容是暴露自己的接口,但是其实现是通过调用其他对象的接口来完成的;而聚合则是直接暴露其他对象的接口,对外保密,客户压根不知道它使用的其实是其他对象的借口。
3. GUID:128位全局唯一标识符。由本机MAC地址(无网卡则用其它随机算法替代)及生成时刻来保证其最大随机性。可用 GUIDgen.exe 自动产生,COM库提供API:HRESULT CoCreateGuid(GUID *pguid); 来产生之(pguid指向其生成的GUID值)。CLSID也使用GUID来定义其标识符。
4. IUnknown接口提供生存期控制(Reference Counting)和接口查询(QueryInterface方法)。
C++:
class IUnkown
{
virtual HRESULT _stdcall QueryInterface(const IID& iid, void **ppv) = 0;
virtual ULONG _stdcall AddRef() = 0;
virtual ULONG _stdcall Release() = 0;
}
注:AddRef 及 Rlease 用于引用计数。
5. 引用计数。记录有多少个有效指针在引用COM对象,引用者在得到指针时计数加 1 ,不再使用指针时计数减 1。计数为 0 时,COM对象将从内存清除自己。注意:复制接口指针(可能是赋值),引用计数也应增加!
6. 引用计数规则:
A. 参数中使用接口指针变量时,
1) 对于输入接口指针参数,被调用函数使用输入参数时不必调用 AddRef 和 Release 函数(输入参数受调用函数控制,因此在被调用函数执行过程中接口指针一定保持有效);
2) 对于输出接口指针参数,被调用函数在返回输出参数前,应调用 AddRef 使接口引用计数增 1 (输出参数相当于在被调用函数中生成了一个新的接口指针变量);
3) 对于输入输出接口指针参数,在参数被修改之前,对原来传进来的接口指针调用 Release 使引用计数减 1 。在参数被修改之后,对新的接口指针变量调用 AddRef 使引用
计数增 1。如果参数没有修改,则类似于输入参数处理。
B. 局部接口指针变量。可以不调用 AddRef 和 Rlease 方法(因为在局部函数块中,接口指针变量总是有效的)。
C. 全局接口指针变量。作为输入参数传给某个函数前调用 AddRef , 函数执行返回后调用 Rlease,以保证函数调用过程中可以使用该全局接口指针变量。
D. C++ 中类成员变量在类的作用域内等同于C类别处理。
E. 以上情形都不适合时,使用一般性规则:
1) 在顺序执行过程中,如果要对一个接口指针变量进行赋值,则对赋值后的接口指针变量调用 AddRef 方法,如果赋值前接口指针变量还没有结束,先 Release 之;
2) 结束使用一个接口指针变量,以后不再使用,则调用 Release 方法。
7. 接口查询。
HRESULT QueryInterface([in] REFIID iid, [out] void **ppv);
函数的输入参数 iid 为接口标识符 IID ,输出参数 ppv 为查询得到的目标接口指针,如果对象没有实现 iid 所标识的接口,则输出参数 ppv 指向空(NULL)。
返回值为32位整数,反映了查询结果:
1) S_OK, 查到了指定的接口 *ppv 为接口指针;
2) E_NOINTERFACE,对象不支持所指定的接口,*ppv 为NULL;
3) E_UNEXPECTED,发生了意外错误,*ppv 为NULL;