ATL CComPtr和CComQIPtr详解
CComPtr和CComQIPtr是智能接口指针类,它们在销毁的时候,不需要手动去释放接口指针,在赋值的时候,也不需要手动的AddRef,在出现异常的时候,会自动处理异常,而不需要额外的异常处理代码。
CComPtr和CComQIPtr的不同的地方:CComPtr只能创建固定的特定的接口指针实例。而CComQIPtr不但实现了CComPtr的所有的功能,而且当我们把一个不同类型的接口指针赋值给CComQIPtr的时候,CComQIPtr会自动的调用接口指针的QueryInterface接口,来获得对应的正确的接口指针。
(1) 构造函数
第一个参数为智能接口指针的类型,第二个参数为 智能指针的接口ID。
CComPtr<IUnknown> punk;
下面三个例子完全相同
CComPtr<IXXX> pno;
CComPtr<IXXX,&__uuidof(IXXX)> pno;
CComPtr<IXXX,&IID_IXXX> pno;
CComQIPtr可以用任何一个类型的接口指针初始化,如果初始化的值与CComQIPtr的类型相同,那么构造函数简单调用 指针的AddRef,但是,如果类型不同的话,它会先调用指针的QueryInterface来获得相同的类型的接口指针,当QueryInterface失败的话,内部指针会被设置为NULL。
所有下面的代码,可以用来检测是否转换成功:
void Func(IUnknown * punk)
{
CComQIPtr<IXXX> pno(punk);
if(pno)
{
//正确转换
pno->doSomething();
}
}
(2) 赋值
赋值的时候,发生下面三件事:
- 如果当前指针不为空,那么释放当前指针
- 如果源指针不为空,那么AddRef
- 将当前指针设置为源指针
oprator = 可以让我们把任何一个CComPtr赋值给CComQIPtr对象,如果有必要就会调用QueryInterface .
例如:
CComQIPtr<IFoo> fooPtr;
CComQIPtr<IBar> barPtr;
barPtr = fooPtr;
(3) CoCreateInstance方法
CComPtr提供了一个实例化对象的方法
HRESULT CoCreateInstance(REFCLSID rclsid,LPUNKNOWN pUnkOuter=NULL, DWORD dwClsCOntext=CLSCTX_ALL);
HRESULT CoCreateInstance(LPCOLESTR szProgID, LPUNKNOSWN pUnkOuter=NULL, DWORD dwClsContxt=CLSCTX_ALL);
后两个参数不管,第一个参数要不传入CLSID,要不传入字符串形式的progID.
例子代码如下:
CComPtr<IXXX> ptr;
HRESULT hr=ptr.CoCreateInstance(__uuidof(IXXX));
(4) operator *()
当对CComPtr解除指针的引用时,跟普通的指针一样,返回一个内部指针类型的引用。
(5) operator &()
获取智能指针对象的地址,实际上也是返回内部指针的地址,跟普通指针一样。
(6) 在调用ComUninitialize方法前,需要手动释放所有的全局或者静态变量。
如何手动释放呢:
- 设置指针指向NULL
- 调用Release方法,注意是要调用智能指针的Release方法,而不是内部指针的,指针指针的Release方法,会调用内部指针的Relase,然后设置内部指针为NULL,这样就可以防止多次释放接口。
(7) CopyTo方法,拷贝后的智能指针生命周期完全独立。
我们用CopyTo方法,将指针拷贝到一个out型参数。
(8) Detach 和Attach方法
在返回一个我们不再需要的接口指针给调用者的时候,我们可以用Deatch方法。
当我们需要把原始指针的所有权转移到智能指针时候,用Attach方法。
(9) QueryInterface方法
他只需要传入期望获得的接口类型的变量地址,即可。
CComPtr<IFoo> pFoo=...;
CComPtr<IBar> pBar;
HRESULT hr = pFoo.QueryInterface(&pBar);
(10) IsEqualObject方法
IsEqualObject方法用来判断两个接口指针释放引用的是同一个对象。
(11) != 和 == 操作符
跟普通的 一样
(12) CComPtr对IDispatch的特化
CComPtr<IDispatch> iptr;
属性调用的辅助函数:
- GetIDOfName(LPCOLESTR lpsz,DISPID * pdispid) 这个方法,获得属性的DISPID
- GetProPerty(DISPID dwDispid, VARIANT * pVar) 这个方法,获得属性。SetProPerty(DISPID dwDispid, VARIANT * pVar) 这个方法,设置属性。
- GetPropertyByName(LPCOLESTR lpsz, VARIANT * pVar) ,SetPropertyByName(LPCOLESTR lpsz, VARIANT * pVar) 直接通过名称,获得和设置属性。
方法调用的辅助函数:
- HRESULT Invoke0(DISPID dispid,VARIANT * pvarRet=NULL); 通过DISPID调用 没有参数的方法。
- HRESULT Invoke0(LPCOLESTR lpszName, VARIANT * pvarRet=NULL); 通过方法名称,调用没有参数的方法。
- HRESULT Invoke1(DISPID dispid,VARIANT * param1, VARIANT * pvarRet=NULL); 通过DISPID调用 有一个参数的方法。
- HRESULT Invoke1(LPCLOESTR lpszName ,VARIANT * param1, VARIANT * pvarRet=NULL); 通过方法名称,调用有一个参数的方法。
- HRESULT InvokeN(DISPID dispid,VARIANT* params, int nParams, VARIANT * pvarRet=NULL); 通过DISPID调用有N个参数的方法。
- HRESULT InvokeN(LPCLOESTR lpszName ,VARIANT * params,int nParams, VARIANT * pvarRet=NULL); 通过方法名称调用有N个参数的方法。
注意,通过参数列表的方法调用的时候,参数是反向的顺序,最后一个参数是元素0。 切记。