最近在看潘爱民写的《com的原理和应用》,看到第四章关于com的聚合的例子不是很明白
。例子中有两个类CompB 和CompA. 其中Comp聚合了CompA.CompA实现了ISomeInterface和
INondelegatingUnknown, ISomeInterface继承了IUnkown.其中INondelegatingUnknown的申明如下:class INondelegatingUnknown
{
public:
virtual HRESULT  __stdcall  NondelegationQueryInterface(const IID& iid, void 
**ppv) = 0 ;
virtual ULONG  __stdcall  NondelegatingAddRef() = 0; 
virtual ULONG  __stdcall  NondelegationRelease() = 0;
};类CompB的类厂在创建CompB的实例的时候,调用CoCreateInstance方法(CLSID_CompA, pU
nknownOuter, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)& m_pUnknownInner)来
创建类CompA的实例。这个方法又间接调用了类厂CAFactory的CreateInstance的函数,函
数的定义如下。HRESULT CAFactory::CreateInstance(IUnknown *pUnknownOuter, 
   const IID& iid, void **ppv)
{
HRESULT hr;
//  iid must be IID_IUnknown for aggregating 
if ( ( pUnknownOuter != NULL ) && ( iid != IID_IUnknown ) )
{
return CLASS_E_NOAGGREGATION;
}    *ppv=NULL;
hr=E_OUTOFMEMORY; //Create the object passing function to notify on destruction.
CA *pObj=new CA (pUnknownOuter);
if (NULL==pObj)
return hr;   
   
//Obtain the first interface pointer (which does an AddRef)
hr = pObj->NondelegationQueryInterface(iid, ppv);   
    if (hr != S_OK) {
  //Kill the object if initial creation or FInit failed.
g_CompANumber --; // Reference count g_CompANumber be added in constructor
delete pObj;
}
   
return hr;   
}
在这个函数里面,当类A的实例生成之后,调用了NondelegationQueryInterface函数查询
IID_IUnknown接口
HRESULT CA::NondelegationQueryInterface(const IID& iid, void **ppv)
{
if ( iid == IID_IUnknown )
{
*ppv = (INondelegatingUnknown *) this ;
((IUnknown *)(*ppv))->AddRef() ;   <---- Here is my question
} else if ( iid == IID_SomeInterface ) 
{
*ppv = (ISomeInterface *) this ;
((ISomeInterface *)(*ppv))->AddRef() ;

else
{
*ppv = NULL;
return E_NOINTERFACE ;
}
return S_OK;
}
然后发现((IUnknown *)(*ppv))->AddRef() 这里实际调用了NondelegatingAddRef()而不
是AddRef方法(我用Visual Studio 2005跟踪调试). 不知道为什么会这样, 有没有熟悉
这本书的人或者com的人能够解释一下。

解决方案 »

  1.   

    这是由多态决定的。估计你的A代码中INondelegatingUnknown是放在前面的。你应该把ISomeInterface放在前面。这样你调用addref就不会发生这种情况。
    ---------
    //Obtain the first interface pointer (which does an AddRef) 
    hr = pObj->NondelegationQueryInterface(iid, ppv);  
        if (hr != S_OK) { 
    ---------这就是就是访问第一个继承的接口,实际上,他误认为你的INondelegatingUnknown为ISomeInterface。一家之言。
      

  2.   

    INondelegatingUnknown 是从IUnknown派生的吗?它实现了AddRef吗?如果实现了,调用了NondelegatingAddRef?
      

  3.   

    INondelegatingUnknown跟IUnknown是具有相同内存结构的虚拟函数表
    在组件不被聚合的时候调用的是INondelegatingUnknown的函数
      

  4.   

    INondelegatingUnknow不从IUnknown派生。
    INondelegatingUnknow的申明如下:class INondelegatingUnknown
    {
    public:
    virtual HRESULT  __stdcall  NondelegationQueryInterface(const IID& iid, void **ppv) = 0 ;
    virtual ULONG  __stdcall  NondelegatingAddRef() = 0; 
    virtual ULONG  __stdcall  NondelegationRelease() = 0;
    };然后我修改了一下这个接口 NondelegatingAddRef()和 NondelegationRelease()的申明的顺序, 就是把NondelegationRelease放到NondelegationAddRef前面,发现这样的话,上面的代码
    ((IUnknown *)(*ppv))->AddRef() ;实际调用的就是NondelegationRelease()。 呵呵, 感觉有点和多继承下vptr和vtable在内存中的模型有关系。 看来我要把inside C++ object model 这本书翻出来好好看看了。