COM初体验
以前在我学校里培训过一段时间C++,我敬爱的吴老师略有提及。那个时候觉得COM遥不可及,觉得,哇塞好神圣。我觉得自己啥都没学好,我不应该这么早去涉及这片过于光荣的领地。既没有觉悟也没有动力去迎接这样一场学习。让对于COM的学习一拖再拖,就像拖延症。然而现实总是残酷的这项技术早已经不再神秘不再光荣依旧,技术的发展甩给我狠狠地一记巴掌,如果连这种技术都不了解确实很难混下去了。
#define interface struct
在其它开发平台下,也可以自己编写预定义代码
COM组件与COM接口:
1 interface IUnknown 2 { 3 virtual HRESULT QueryInterface(const IID &iid, void **ppv) = 0; 4 virtual ULONG AddRef() = 0; 5 virtual ULONG Release() = 0; 6 };
QueryInterface:
可以通过QueryInterface函数来查询某个组件是否支持某个特定的接口。若支持,QueryInterface返回一个指向此接口的指针。这里我们看到函数返回类型为HRESULT,参数其中一个的类型是const IID&。HRESULT跟IID是什么呢?
typedef struct _GUID { DWORD Data1; //随机数 WORD Data2; //和时间相关 WORD Data3; //和时间相关 BYTE Data4[8]; //和网卡MAC相关 } GUID;
GUID:
// {0E04C466-6CE9-4513-B306-43E8F7025EB9}
static const GUID guid =
{ 0xe04c466, 0x6ce9, 0x4513, { 0xb3, 0x6, 0x43, 0xe8, 0xf7, 0x2, 0x5e, 0xb9 } };
QueryInterface的实现:
virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, void **ppv) { if (iid == IID_IUnknown) { //即使CA继承了两个IUnknown接口,其中一个来自于IX,另一个来自于IY。我们一般返回第一个被继承的IX接口。 *ppv = static_cast<IX*>(this); } else if (iid == IID_IX) { //返回IX接口 *ppv = static_cast<IX*>(this); } else if (iid == IID_IY) { //返回IY接口 *ppv = static_cast<IY*>(this); } else { //查询不到IID,*ppv返回NULL。 *ppv = NULL; return E_NOINTERFACE; //函数返回值返回E_NOINTERFACE,表示组件不支持iid的接口。 } //查询成功时,需要自增引用计数 AddRef(); return S_OK; //返回S_OK }
引用计数的原理:
1 virtual ULONG STDMETHODCALLTYPE AddRef() 2 { 3 //简单实现方法 4 return ++m_lCount; 5 6 //多线程编程采用如下方法,这种方法确保同一个时刻只会有一个线程来访问成员变量 7 //return InterlockedIncrement(&m_lCount); 8 } 9 10 virtual ULONG STDMETHODCALLTYPE Release() 11 { 12 //简单实现方法 13 if (--m_lCount == 0) 14 { 15 delete this; //销毁自己 16 return 0; 17 } 18 return m_lCount; 19 20 ////多线程编程采用如下方法,这种方法确保同一个时刻只会有一个线程来访问成员变量 21 //if (InterlockedDecrement(&m_lCount) == 0) 22 //{ 23 // delete this; //销毁自己 24 // return 0; 25 //} 26 //return m_lCount; 27 }
引用计数的优化:
一、输入参数原则:输入参数指的是给函数传递某个值的参数。在函数体中将会使用这个值但却不会修改它或将其返回给调用者。在C++中,输入参数实际上就是那些按值传递的参数。对传入函数的接口指针,无需调用AddRef与Release
二、局部变量原则对于局部复制的接口指针,由于它们只是在函数的生命期内才存在,因此无需调用AddRef与Release
1 void Fun(IX *pIXParam) //参数传递存在赋值过程 2 { 3 //pIXParam->AddRef(); //可优化,注释掉 4 pIXParam->Fx1(); 5 pIXParam->Fx2(); 6 //pIXParam->Release(); //可优化,注释掉 7 }
1 void Fun(IX *pIX) 2 { 3 IX *pIX2 = pIX; 4 //pIX2->AddRef(); //可优化,注释掉 5 pIX2->Fx1(); 6 pIX2->Fx2(); 7 //pIX2->Release(); //可优化,注释掉 8 }
void Fun(IX **ppIX) { (*ppIX)->Fx1(); (*ppIX)->Fx2(); (*ppIX)->Release(); //可以优化吗? *ppIX = m_pIXOther; (*ppIX)->AddRef(); //可以优化吗? (*ppIX)->Fx1(); (*ppIX)->Fx2(); }
//以上两句务必要运行,因为*ppIX 与m_pIXOther不一个属性同一个组件。
//比如假设*ppIX是指向第一次的new CA(),而m_pIXOther却是指向第二次的new CA()。
//或者*ppIX是指向new CA(),而m_pIXOther是指向new CZ(),CA与CZ的共同点,只是都继承了IX接口而已。