ATL CComPtr和CComQIPtr详解

CComPtr和CComQIPtr是智能接口指针类,它们在销毁的时候,不需要手动去释放接口指针,在赋值的时候,也不需要手动的AddRef,在出现异常的时候,会自动处理异常,而不需要额外的异常处理代码。

CComPtrCComQIPtr的不同的地方: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) 赋值

赋值的时候,发生下面三件事:

  1. 如果当前指针不为空,那么释放当前指针
  2. 如果源指针不为空,那么AddRef
  3. 将当前指针设置为源指针

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方法前,需要手动释放所有的全局或者静态变量。

如何手动释放呢:

  1. 设置指针指向NULL
  2. 调用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;

 属性调用的辅助函数:

  1. GetIDOfName(LPCOLESTR  lpsz,DISPID  * pdispid)  这个方法,获得属性的DISPID
  2. GetProPerty(DISPID  dwDispid,  VARIANT *  pVar)  这个方法,获得属性。SetProPerty(DISPID  dwDispid,  VARIANT *  pVar)  这个方法,设置属性。
  3. GetPropertyByName(LPCOLESTR lpsz, VARIANT * pVar) ,SetPropertyByName(LPCOLESTR lpsz, VARIANT * pVar) 直接通过名称,获得和设置属性。

方法调用的辅助函数:

  1. HRESULT  Invoke0(DISPID dispid,VARIANT * pvarRet=NULL); 通过DISPID调用 没有参数的方法。
  2. HRESULT  Invoke0(LPCOLESTR  lpszName, VARIANT * pvarRet=NULL); 通过方法名称,调用没有参数的方法。
  3. HRESULT  Invoke1(DISPID dispid,VARIANT * param1, VARIANT * pvarRet=NULL); 通过DISPID调用 有一个参数的方法。
  4. HRESULT  Invoke1(LPCLOESTR  lpszName ,VARIANT * param1, VARIANT * pvarRet=NULL); 通过方法名称,调用有一个参数的方法。
  5. HRESULT  InvokeN(DISPID dispid,VARIANT* params, int  nParams,   VARIANT * pvarRet=NULL); 通过DISPID调用有N个参数的方法。
  6. HRESULT InvokeN(LPCLOESTR   lpszName ,VARIANT * params,int  nParams,  VARIANT * pvarRet=NULL); 通过方法名称调用有N个参数的方法。

注意,通过参数列表的方法调用的时候,参数是反向的顺序,最后一个参数是元素0。  切记。

ATL CComPtr CComQIPt

分享到:
评论加载中,请稍后...
创APP如搭积木 - 创意无限,梦想即时!
回到顶部