iq50 发表于 2010-2-10 16:16:19

模拟COM组件聚合的问题

// my_test_com8.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <Windows.h>
#include <iostream>
using namespace std;

//不考虑内存泄露


#defineIID_MYIUnknown 1000
#defineIID_IX         1001
#defineIID_IY         1002
#defineIID_IZ         1003

#defineCLSID_Component1 1024
#defineCLSID_Component2 1025



class MyTrace
{
public:
        MyTrace(const char *msg)
        {
                memset(buff, 0, 1024);
                printf(buff,msg);
                cout << ">>>" << buff << endl ;
        }


        ~MyTrace()
        {

                cout << ">>>" << buff << endl ;
        }
private:
        char buff;
};
//-------------------------------------------------------------------

struct INondelegatingUnknown
{
public:
        virtual int __stdcall NondelegatingQueryInterface(const int& iid, void** ppv) = 0 ;
        virtual int __stdcall NondelegatingAddRef() = 0 ;
        virtual int __stdcall NondelegatingRelease() = 0 ;
} ;


class MYUnknown
{
public:
        virtual int __stdcall QueryInterface(const int& iid, void** ppv)= 0;
        virtual int   __stdcall AddRef()= 0;
        virtual int   __stdcall Release() = 0;
};

class IX : public MYUnknown
{
public:
        virtual void __stdcall Fx() = 0 ;
};


class IY : public MYUnknown
{
public:
        virtual void __stdcall Fy() = 0 ;
};


class IZ : public MYUnknown
{
public:
        virtual void __stdcall Fz() = 0 ;
};
//////////////////////////////////////////////////////////////////////////////////
void trace2(const char* msg) { cout << "Component 2:\t" << msg << endl ;}
class CB : public IY,
        public INondelegatingUnknown
{
public:
        // Delegating IUnknown
        virtual int __stdcall
                QueryInterface(const int& iid, void** ppv)
        {
                trace2("Delegate QueryInterface.") ;
                return m_pUnknownOuter->QueryInterface(iid, ppv) ;
        }

        virtual int __stdcall AddRef()
        {
                trace2("Delegate AddRef.") ;
                return m_pUnknownOuter->AddRef() ;
        }

        virtual int __stdcall Release()
        {
                trace2("Delegate Release.") ;
                return m_pUnknownOuter->Release() ;
        }

        // Nondelegating IUnknown
        virtual int __stdcall
                NondelegatingQueryInterface(const int& iid, void** ppv) ;
        virtual int   __stdcall NondelegatingAddRef() ;
        virtual int   __stdcall NondelegatingRelease() ;

        // Interface IY
        virtual void __stdcall Fy() { cout << "Fy" << endl ;}

        // Constructor
        CB(MYUnknown* m_pUnknownOuter) ;

        // Destructor
        ~CB() ;

private:
        long m_cRef ;

        MYUnknown* m_pUnknownOuter ;
} ;

//
// IUnknown implementation
//
int __stdcall CB::NondelegatingQueryInterface(const int& iid,
                                                                                                  void** ppv)
{
        //MyTrace say("CB::NondelegatingQueryInterface");
        if (iid == IID_MYIUnknown)
        {
                // !!! CAST IS VERY IMPORTANT !!!
                *ppv = static_cast<INondelegatingUnknown*>(this) ;// @N
        }
        else if (iid == IID_IY)
        {
                *ppv = static_cast<IY*>(this) ;
        }
        else
        {
                *ppv = NULL ;
                return 2 ;
        }
        reinterpret_cast<IUnknown*>(*ppv)->AddRef() ;
        return 0 ;
}

int __stdcall CB::NondelegatingAddRef()
{
        return ::InterlockedIncrement(&m_cRef) ;
}

int __stdcall CB::NondelegatingRelease()
{
        if (::InterlockedDecrement(&m_cRef) == 0)
        {
                delete this ;
                return 0 ;
        }
        return m_cRef ;
}

//
// Constructor
//
CB::CB(MYUnknown* pUnknownOuter)
: m_cRef(1)
{
        ;

        if (pUnknownOuter == NULL)
        {
                trace2("Not aggregating; delegate to nondelegating IUnknown.") ;
                m_pUnknownOuter = reinterpret_cast<MYUnknown*>
                        (static_cast<INondelegatingUnknown*>
                        (this)) ;
        }
        else
        {
                trace2("Aggregating; delegate to outer IUnknown.") ;
                m_pUnknownOuter = pUnknownOuter ;
        }
}

//
// Destructor
//
CB::~CB()
{

        trace2("Destroy self.") ;
}

/////////////////////////////////////////////////////////////////////////
void trace1(const char* msg) { cout << "Component 1:\t" << msg << endl ;}
class CA : public IX
        // public IY @N
{
public:
        // IUnknown
        virtual int __stdcall QueryInterface(const int& iid, void** ppv) ;
        virtual int   __stdcall AddRef() ;
        virtual int   __stdcall Release() ;

        // Interface IX
        virtual void __stdcall Fx() { cout << "Fx" << endl ;}

        /* @N Component1 aggregates instead of implementing interface IY.
        // Interface IY
        virtual void __stdcall Fy() { m_pIY->Fy() ;}
        */

        // Constructor
        CA() ;

        // Destructor
        ~CA() ;

        // Initialization function called by the class factory
        // to create the contained component
        int __stdcall Init() ;// @N

private:
        // Reference count
        long m_cRef ;

        // Pointer to the aggregated component's IY interface
        // (We do not have to retain an IY pointer. However, we
        // can use it in QueryInterface.)
        IY* m_pIY ;               // @N

        // Pointer to inner component's IUnknown
        MYUnknown* m_pUnknownInner ; // @N
} ;

//
// Constructor
//
CA::CA()
: m_cRef(1),
m_pUnknownInner(NULL) //@N
{
       
}

//
// Destructor
//
CA::~CA()
{
       
        trace1("Destroy self.") ;

        // Prevent recursive destruction on next AddRef/Release pair.
        m_cRef = 1 ;

        // Counter the pUnknownOuter->Release in the Init method.
        MYUnknown* pUnknownOuter = this ;
        pUnknownOuter->AddRef() ;

        // Properly release the pointer; there might be per-interface
        // reference counts.
        m_pIY->Release() ;

        // Release contained component.
        if (m_pUnknownInner != NULL)   // @N
        {
                m_pUnknownInner->Release() ;
        }
}

// Initialize the component by creating the contained component.
int __stdcall CA::Init()
{
        // Get the pointer to the outer unknown.
        // Since this component is not aggregated, the outer unknown
        // is the same as the this pointer.
        MYUnknown* pUnknownOuter = (MYUnknown*)this ;

        trace1("Create inner component.") ;
        CB *pCB = new CB(pUnknownOuter);
        pCB->NondelegatingQueryInterface(IID_MYIUnknown , (void **)&m_pUnknownInner);
        int hr = m_pUnknownInner == NULL ? -1:0;
        /*
                ::CoCreateInstance(CLSID_Component2,                
                pUnknownOuter, // Outer component's IUnknown @N
                CLSCTX_INPROC_SERVER,
                IID_IUnknown,// IUnknown when aggregating@N
                (void**)&m_pUnknownInner) ;
                */
        if (hr == -1)
        {
                trace1("Could not create contained component.") ;
                return -1 ;
        }

        // This call will increment the reference count on the outer component.
        trace1("Get the IY interface from the inner component.") ;
        hr = m_pUnknownInner->QueryInterface(IID_IY, (void**)&m_pIY) ; //@N
        if (hr == -1)
        {
                trace1("Inner component does not support interface IY.") ;
                m_pUnknownInner->Release() ;
                return -1 ;
        }

        // We need to release the reference count added to the
        // outer component in the above call.So call Release
        // on the pointer you passed to CoCreateInstance.
        pUnknownOuter->Release() ; //@N
        return 0 ;
}

//
// IUnknown implementation
//
int __stdcall CA::QueryInterface(const int& iid, void** ppv)
{        
        if (iid == IID_MYIUnknown)
        {
                *ppv = static_cast<MYUnknown*>(this) ;
        }
        else if (iid == IID_IX)
        {
                *ppv = static_cast<IX*>(this) ;
        }
        else if (iid == IID_IY)
        {
                trace1("Return inner component's IY interface.") ;
#if 1
                // You can query for the interface.
                return m_pUnknownInner->QueryInterface(iid,ppv) ; //@N
#else
                // Or you can return a cached pointer.
                *ppv = m_pIY ;   //@N
                // Fall through so it will get AddRef'ed
#endif
        }
        else
        {
                *ppv = NULL ;
                return 2 ;
        }
        reinterpret_cast<MYUnknown*>(*ppv)->AddRef() ;
        return 0 ;
}

int __stdcall CA::AddRef()
{
        return ::InterlockedIncrement(&m_cRef) ;
}

int __stdcall CA::Release()
{
        if (::InterlockedDecrement(&m_cRef) == 0)
        {
                delete this ;
                return 0 ;
        }
        return m_cRef ;
}
/////////////////////////////////////////////////////////////////////////////////

int _tmain(int argc, _TCHAR* argv[])
{
        CA *pCA = new CA();
    pCA->Init();
        IX *pIX = NULL;
        int rs = pCA->QueryInterface(IID_IX,(void **)&pIX);

        if (pIX)
        {
                pIX->Fx();
        }

        IY * pIY = NULL;
    pIX->QueryInterface(IID_IY,(void **)&pIY);

        if (pIY)
        {
                pIY->Fy();
                pIY->Release() ;
        }
        pIX->Release() ;

        return 0;
}

iq50 发表于 2010-2-10 16:18:55

为什么在CA::Init()中(302行)
hr = m_pUnknownInner->QueryInterface(IID_IY, (void**)&m_pIY) ;
调用的是
CB类的NondelegatingQueryInterface(const int& iid, void** ppv) ;
而不是
CB类的QueryInterface ??????

winston 发表于 2010-2-10 16:25:46

出了啥问题?
你问的问题,属于COM里面的接口调用问题,具体内容我已经忘记了,不过可以查一下COM本质论,里面有说明。

iq50 发表于 2010-2-10 16:44:28

这是《COM技术内幕》第8章的代码,因为原来的范例代码只有一个MAKEFILE,不好调试,我就把他的逻辑拿出来,不用组件方式,其他都一样的。VS2003下编译可运行,问题就是
CA::Init()中(302行)
hr = m_pUnknownInner->QueryInterface(IID_IY, (void**)&m_pIY) ;
调用的是
CB类的NondelegatingQueryInterface(const int& iid, void** ppv) ;
而不是
CB类的QueryInterface

多谢了, 我去看看那本书

dwh0403 发表于 2010-2-10 16:56:47

参考http://blog.chinaunix.net/u2/66646/showart_673644.html


m_pUnknownInner是从pCB->NondelegatingQueryInterface(IID_MYIUnknown , (void **)&m_pUnknownInner); 中得来的,


int __stdcall CB::NondelegatingQueryInterface(const int& iid,
                                                                                                void** ppv)
{
      //MyTrace say("CB::NondelegatingQueryInterface");
      if (iid == IID_MYIUnknown)
      {
                // !!! CAST IS VERY IMPORTANT !!!
                *ppv = static_cast<INondelegatingUnknown*>(this) ;// @N
      }
      else if (iid == IID_IY)

}

hr = m_pUnknownInner->QueryInterface(IID_IY, (void**)&m_pIY) ; //@N

==
(IUnknown *)(INonDelegatingUnknown *)this-> QueryInterface实际上调用的是NonDelegatingQueryInterface。

iq50 发表于 2010-2-11 11:02:03

m_pUnknownInner被声明为MYUnknow * ,但他其实是 m_pUnknownInner = (MYUnknow * )static_cast<INondelegatingUnknown*>(CB* this);尽管m_pUnknownInner 指向的内存是按MYUnknow排布(包括虚函数表),但实际装载的是INondelegatingUnknown类型的内容,所以m_pUnknownInner->QueryInterface会跳转到CB::NondelegatingQueryInterface。如果改变INondelegatingUnknown的内存布局,即改变INondelegatingUnknown的虚函数声明顺序,那么这种做法就不合需求了。可以把virtual int __stdcall NondelegatingAddRef() = 0 ;声明在第一个。m_pUnknownInner->QueryInterface会跳转到CB::NondelegatingAddRef() 而不是CB::NondelegatingQueryInterface()。
真的很酷!
页: [1]
查看完整版本: 模拟COM组件聚合的问题