天天看點

COM學習筆記(一):入門及QueryInterface

#include <objbase.h>定義了 ->          #define interface struct

#include <windef.h>定義了  ->#define pascal __stdcall

//*****************************************************************************

//COM雛形之前身

 #include <iostream.h>

 #include <objbase.h>

  trace(const char* pMsg){

cout<<pMsg<<endl;

}

 interface IX{

  virtual void __stdcall Fx1() = 0;

virtual void __stdcall Fx2() = 0;

};

 interface IY{

virtual void __stdcall Fy1() = 0;

virtual void __stdcall Fy2() = 0;

};

 class CA : public IX,public IY{

 public:

virtual void __stdcall Fx1(){cout<<"CA::Fx1"<<endl;}

virtual void __stdcall Fx2(){cout<<"CA::Fx2"<<endl;}

virtual void __stdcall Fy1(){cout<<"CA::Fy1"<<endl;}

virtual void __stdcall Fy2(){cout<<"CA::Fy2"<<endl;}

};

 int main(){

 trace("Client: Create an instance of the component.");

CA *pA = new CA;

 IX *pIX = pA;

trace("Client:Use the IX interface.");

pIX->Fx1();

pIX->Fx2();

 IY *pIY = pA;

trace("Client:Use the IY interface.");

pIY->Fy1();

pIY->Fy2();

 race("Client: Delete the component.");

 delete pA;

 return 0;

}

總結:

一:COM接口在C++中是用純抽象基類實作的。

二:一個COM元件可以提供多個接口。

三:一個C++類可以使用多繼承來實作一個可以提供多個接口的元件。

四:用__stdcall标記的函數将使用标準的調用約定,即 這些函數将在傳回到調用者之前将參數從棧中删除。Microsoft平台上COM接口所提供的所有函數使用的軍事标準的調用約定。

 //*****************************************************************************

 定義一個純抽象基類也就是定義了相應的記憶體結構。但此記憶體隻是在派生類中實作此抽象基類時才會被配置設定。當派生類繼承一個抽象基類時,它将繼承此記憶體結構。COM接口的記憶體結構同C++編譯器為抽象基類所生成的記憶體結構是相同的。

所有的COM接口都必須繼承一個名為IUnknown的接口。這意味着所有COM接口的前三項都是相同的,其中儲存的是IUnknown中三個成員函數的實作的位址。

 //*****************************************************************************

Interface IUnknown{

HRESULT __stdcall QueryInterface(const IID& iid,void** ppv) = 0;

virtual ULONG __stdcall AddRef() = 0;

virtual ULONG __stdcall Release() = 0;

};

1) 接口查詢:定制QueryInterface,客戶調用之(若支援,傳回一個指向此接口的指針,否則傳回一個錯誤代碼)。

若某個接口的vtbl中的前三個函數不是IUnknown的三個成員函數,它将不是一個COM接口。

2) 擷取IUknown指針:IUnknown* CreateInstance();它可以建立一個元件并傳回一個IUnknown指針,而不必再使用new操作符。(需要自己定制)

eg:

void foo(IUnknown* pI){

IX* pIX = NULL;

HRESULT hr = pI->QueryInterface(IID_IX,(void**)&pIX);

If(SUCCEEDED(hr)){

pIX->Fx();

}

}

HRESULT __stdcall CA::QueryInterface(const IID& iid,void**ppv){

 if(iid == IID_IUnknown){

*ppv = static_cast<IX*>(this);

}

else if(iid == IID_IX){

*ppv = static_cast<IX*>(this);

}

else if(iid == IID_IY){

*ppv = static_cast<IY*>(this);

}

else{

*ppv = NULL;

return E_NOINTERFACE;

}

static_cast<IUnknown*>(*ppv)->AddRef();

return S_OK;

}

 //*****************************************************************************

 一個完整的例子:

// IUnknown.cpp

#include <iostream.h>

#include <objbase.h>

 void trace(const char* msg){cout<<msg<<endl;}

interface IX : IUnknown{

virtual void __stdcall Fx() = 0;

};

interface IY : IUnknown{

virtual void __stdcal Fy() = 0;

};

interface IZ : IUnknown{

virtual void __stdcall Fz() = 0;

};

extern const IID IID_IX;

extern const IID IID_IY;

extern const IID IID_IZ;

class CA : public IX,

   public IY{

virtual HRESULT __stdcall QueryInterface(const IID& IID,void** ppv);

virtual ULONG __stdcall AddRef(){return 0;}

virtual ULONG __stdcall Release(){return 0;}

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

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

};

HRESULT __stdcall CA::QueryInterface(const IID& iid,void ** ppv){

if(iid == IID_IUnknown){

trace("QueryInterface: Return pointer to IUknown.");

*ppv = static_cast<IX*>(this);

}else if(iid == IID_IX){

trace("QueryInterface: Return pointer toIX.");

*ppv = static_cast<IX*>(this);

}else if(iid == IID_IY){

trace("QueryInterface: Return pointer toIY.");

*ppv = static_cast<IY*>(this);

}else{

trace("QueryInterface:Interface not supported.");

*ppv = NULL;

return E_NOINTERFACE;

}

reinterpret_cast<IUnknown*>(*ppv)->AddRef();

return S_OK;

}

IUnknown* CreateInstance(){

IUnknown* pI = static_cast<IX*>(new CA);

pI->AddRef();

return pI;

}

static const IID IID_IX = {0x32bb8320,0xb41b,0x11cf,{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};

static const IID IID_IY = {0x32bb8321,0xb41b,0x11cf,{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};

static const IID IID _IZ =  {0x32bb8322,0xb41b,0x11cf,{0xa6,0xbb,0x0,0x80,0xc7,0xb2,0xd6,0x82}};

// Client

int main(){

HRESULT hr;

trace("Client:Get an IUnknown pointer.");

IUnknown* pInknown = CreateInstance();

trace("Client:Get interface IX.");

IX* pIX = NULL;

hr = pIUnknown->QueryInterface(IID_IX,(void **)&pIX);

if(SUCCEEDED(hr)){

trace("Client:Succeeded getting IX.");

pIX->Fx();

}

trace("Client: Get interface IY.");

IX* pIY = NULL;

hr = pIUnknown->QueryInterface(IID_IY,(void **)&pIY);

if(SUCCEEDED(hr)){

trace("Client: Succeeded getting IY.");

pIX->Fy();

}

  trace("Client:Ask for an unsupported interface.");

IZ* pIZ = NULL;

hr = pIUnknown->QueryInterface(IID_IZ,(void**)&pIZ);

if(SUCCEEDED(hr)){

trace("Client:Succeeded in getting interface IZ.");

pIZ->Fz();

}else{

trace("Client:Could not get interface IZ.");

trace("Client:Get interface IY from interface IX.");

IY* pIYfromIX = NULL;

hr = pIX->QueryInterface(IID_IY,(void **)&pIYfromIX);

if(SUCCEEDED(hr)){

trace("Client:  Succeeded getting IY.");

pIyfromIX->Fy();

}

trace("Client:Get interface IUnknown from IY.");

IUnknown* pIUnknownFromIY = NULL;

hr = pIY->QueryInterface(IID_IUnknown,(void**)&pIUnknownFromIY);

if(SUCCEEDED(hr)){

cout<<"Are the IUnknown pointers equal?";

if(pIUnknownFromIY == pIUnknwon){

cout<<"Yes,pUnknownFromIY == pIUnknown."<<endl;

}else{

cout<<"No,pIUnknownFromIY != pIUnknown."<<endl;

}

}

delete pIUnknown;

return 0;

}

//*****************************************************************************

 QueryInterface的實作規則:

1.QueryInterface傳回的總是同一IUnknown指針。

2.若客戶曾經擷取過某個接口,那麼它将總能擷取此接口。

3.客戶可以再次擷取已經擁有的接口。

4.客戶可以傳回到起始接口。

5.若能夠從某個接口擷取某特定接口,那麼可以從任意接口都将可以擷取此接口。

繼續閱讀