COM原理
+ -

COM的QueryInterface与NonDelegatingQueryInterface关系

2022-01-08 162 0

COM类的实现如下:

class CMyComponent : public CUnknown, public ISomeInterface
{
public:
    DECLARE_IUNKNOWN;
    STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
    {
        if( riid == IID_ISomeInterface )
        {
            return GetInterface((ISomeInterface*)this, ppv);
        }
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
    }

    CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr)
        : CUnknown(tszName, pUnk, phr)
    {
        /* Other initializations */
    };
    // More declarations will be added later.
};

调用QueryInterface和NonDelegatingQueryInterface的几种形式:

this-> QueryInterface
this-> NonDelegatingQueryInterface
(INonDelegatingUnknown *)this-> NonDelegatingQueryInterface
(IUnknown *)(INonDelegatingUnknown *)this-> QueryInterface

注意:最后一种实际上调用的是NonDelegatingQueryInterface。
下面看看CMyComponent类如何查询接口:

CMyComponent *pCMyComponent = new CMyComponent (L”MyComponent”,NULL,NULL);
ISomeInterface *pSomeInterface = NULL;
pMyComponent->QueryInterface(IID_ISomeInterface,(void**)&pSomeInterface)

在这里使用的是“this-> QueryInterface”方式调用QueryInterface函数。

对象要查询接口,首先调用QueryInterface成员函数进行查询, CMyComponent类通过DECLARE_IUNKNOWN宏定义了QueryInterface成员函数,DECLARE_IUNKNOWN的定义如下:

#define DECLARE_IUNKNOWN                                        \
    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) {      \
        return GetOwner()->QueryInterface(riid,ppv);            \
    };                                                          \
    STDMETHODIMP_(ULONG) AddRef() {                             \
        return GetOwner()->AddRef();                            \
    };                                                          \
    STDMETHODIMP_(ULONG) Release() {                            \
        return GetOwner()->Release();                           \
    };

所以

pMyComponent->QueryInterface(IID_ISomeInterface,(void**)&pSomeInterface)

产生的结果等价于:

GetOwner()->QueryInterface(IID_ISomeInterface,(void**)&pSomeInterface)

下面看GetOwner()函数:

LPUNKNOWN GetOwner() const {
        return m_pUnknown;
};

The utility function CUnknown::GetOwner retrieves a pointer to the IUnknown interface of the component that owns this component. For an aggregated component, the owner is the outer component. Otherwise, the component owns itself. Include the DECLARE_IUNKNOWN macro in the public section of your class definition.

CMyComponent::CMyComponent(IUnknown *pOuterUnkown)
{
    if (pOuterUnknown == NULL)
        m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;
    else
        m_pUnknown = pOuterUnknown;
    [ ... more constructor code ... ]
}

GetOwner获取一个指向该对象(CMyComponent)所属对象的IUnknown接口的指针,如果该对象(CMyComponent)是聚合对象,它的拥有者是外部对象

(m_pUnknown = pOuterUnknown)

否则,此对象拥有自己

(m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this)。

注意m_pUnknown为IUnknown *类型,这一点很重要。

m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;

也将this转换成了IUnknown *对象。

STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) {      \
        return GetOwner()->QueryInterface(riid,ppv);            \
    };

可以看出QueryInterface函数可以改写成下面的形式:

HRESULT QueryInterface(REFIID iid, void **ppv)
{
    return m_pUnknown->QueryInterface(iid, ppv);
}

在这里使用的是“(IUnknown *)(INonDelegatingUnknown *)this-> QueryInterface”方式调用,实际上调用的是NonDelegatingQueryInterface。
因为m_pUnknown是通过语句m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;转换过来的。

注意:这里不是“this-> QueryInterface”,而是“(IUnknown *)(INonDelegatingUnknown *)this-> QueryInterface”,这两者有本质的区别:
如果是“this-> QueryInterface”,这样调用的是DECLARE_IUNKNOWN宏中定义的QueryInterface, 如下所示:

    STDMETHODIMP QueryInterface(REFIID riid, __deref_out void **ppv) {     
        return GetOwner()->QueryInterface(riid,ppv);           
};

如果是(INonDelegatingUnknown *)this-> QueryInterface,将导致编译错误,因为INonDelegatingUnknown接口没有QueryInterface函数。

语句(IUnknown *)(INonDelegatingUnknown *)this先将this转换成INonDelegatingUnknown指针,因为this和INonDelegatingUnknown之间存在继承关系,所以经过转换得到的指针将指向this物体中的INonDelegatingUnknown部分,然后再将 INonDelegatingUnknown指针转换成IUnknown指针,因为INonDelegatingUnknown和IUnknown之间不存在继承关系,所以指针实际上还是指向this物体中的INonDelegatingUnknown部分,而这样做的目的是用一条语句使对于聚合和非聚合都适应,如果是非聚合,m_pUnknown 等于(IUnknown *)(INonDelegatingUnknown *)this,所以m_pUnknown->QueryInterface将调用自己INonDelegatingUnknown接口的 NonDelegatingQueryInterface,而如果是聚合m_pUnknown等于外部对象IUnknown接口指针,则m_pUnknown-> QueryInterface调用的是外部对象IUnknown接口的QueryInterface这样将导致死循环。

来看NonDelegatingQueryInterface的定义:

STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
        if( riid == IID_ISomeInterface )
        {
            return GetInterface((ISomeInterface*)this, ppv);
        }
        return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}

再来看GetInterface函数的定义:

STDAPI GetInterface(LPUNKNOWN pUnk, void **ppv)
{
    CheckPointer(ppv, E_POINTER);
    *ppv = pUnk;
    pUnk->AddRef();
    return NOERROR;
}

注意:这里pUnk->AddRef();调用的是

STDMETHODIMP_(ULONG) AddRef() {                             \
        return GetOwner()->AddRef();                            \
};

而GetOwner()->AddRef();即m_pUnknown-> AddRef();调用的是NonDelegatingAddRef();

class CMyFilter : public CSource
{
……
}

class CMyPin : public CSourceStream,IKsPropertySet,IAMStreamConfig
{
   DECLARE_IUNKNOWN;
   STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv);
   ……
}


HRESULT   __stdcall   CB::NondelegatingQueryInterface(const   IID&   iid,                void**   ppv)  
  {    
  if(iid == IID_IUnknown)  
  {  
  //   !!!   CAST   IS   VERY   IMPORTANT   !!!  
  *ppv   =   static_cast(this)   ;     //   @N  
  }  
  else   if   (iid   ==   IID_IY)  
  {  
  *ppv   =   static_cast(this)   ;   //   注意这一行  
  }  
  else  
  {  
  *ppv   =   NULL   ;  
  return   E_NOINTERFACE   ;  
  }  
  reinterpret_cast(*ppv)->AddRef()   ;  
  return   S_OK   ;  
  }

如果查询IID_IUnknown接口,那么先是调用*ppv = static_cast(this); 经过static_cast的转换,*ppv指向INondelegatingUnknown,而INondelegatingUnknown并不是IUnknown派生得到的,那么语句reinterpret_cast(*ppv)->AddRef()为什么要转化为指针(IUnknown *),明明INondelegatingUnknown不是继承自IUnknown?这样写是为了使代码对聚合和不聚合都适用。语句reinterpret_cast(*ppv)->AddRef()调用的实际上还是INondelegatingUnknown::AddRef();

为什么调用的是INondelegatingUnknown::AddRef()呢?

函数的调用实际是汇编 call 函数地址.对于一个类里定义的函数实际是一个一个的地址偏移.如果地址偏移是一样而且参数个数与排列都一样,函数叫什么名字是没有关系的。

((IUnknown *)(*ppv))->AddRef() ;只是把编译器给骗了,好让它通过. 不过因为偏移与参数都是一样(没参数) 所以就会有那样的结果..
你可以把INondelegatingUnknown的AddRef与Release位置换一下看看是不是就调用了Release()

如果查询IID_IY接口,那么先是调用*ppv = static_cast(this); 经过static_cast的转换,*ppv指向IY,而IY又是从IUnknown派生来的,所以语句reinterpret_cast(*ppv)->AddRef()调用的是IUnknown的AddRef()。

0 篇笔记 写笔记

PortClass 电源管理
电源管理实现的COM是AdapterPowerMgr,继承于IAdapterPowerManagement和CUnknown,用于电源的管理。class AdapterPowerMgr: public IAdapterPowerManagement, public CUnkn......
COM的QueryInterface与NonDelegatingQueryInterface关系
COM类的实现如下:class CMyComponent : public CUnknown, public ISomeInterface{public: DECLARE_IUNKNOWN; STDMETHODIMP NonDelegatingQueryInterface(RE......
COM的QueryInterface与NonDelegatingQueryInterface的测试用例
在COM中,函数的调用实际是汇编 call 函数地址.对于一个类里定义的函数实际是一个一个的地址偏移.如果地址偏移是一样而且参数个数与排列都一样,函数叫什么名字是没有关系的。这里关于这句话做一个测试用例,用于测试COMM的妙用:#include #include......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!