COMノート-QueryInterface関数

クライアントとコンポーネント間の相互作用は、インターフェースを介して完了します。顧客がコンポーネントの他のインターフェースを照会するとき、それはインターフェースを介して行われます。このインターフェイスはIUnknownです。UNKNWN.Hヘッダーファイルで次のように定義されています。

   Interface IUnknown

   {

        virtual HRESULT __stdcall QueryInterface( REFIID riid, void ** ppvObject) = 0;

        virtual ULONG __stdcall  AddRef( void) = 0;

        virtual ULONG __stdcall  Release( void) = 0;

}

すべてのCOMインターフェイスはIUnknownを継承し、各インターフェイスのvtblの最初の3つの関数は、QueryInterface、AddRef、およびReleaseです(図3-1)。このようにして、すべてのCOMインターフェイスをIUnknownインターフェイスとして扱うことができます。

すべてのインターフェイスはIUnknownから継承されるため、すべてのインターフェイスはQueryInterfaceをサポートします。したがって、コンポーネントの任意のインターフェイスを使用して、サポートする他のインターフェイスを取得できます。

QueryInterface
はIUnknown、QueryInterfaceのメンバー関数であり、顧客はこの関数を使用して、コンポーネントが特定のインターフェースをサポートしているかどうかを照会できます。QueryInterfaceがサポートされている場合は、これらのインターフェイスへのポインタを返します。戻り値をサポートしていない場合は、エラーコードになります。

QueryInterfaceには2つのパラメーターがあり、HRESULTの戻り値があります

HRESULT __stdcall QueryInterface(REFIID riid、void ** ppvObject);

最初のパラメーター:インターフェース識別子(IID)

2番目のパラメーター:要求されたインターフェースポインターが保管されているアドレス。

戻り値:クエリが成功するとS_OKが返され、失敗すると対応するエラーコードが返されます。

QueryInterfaceの使用voidfoo
(IUnknown * pI)

{{

// 定义一个接口指针

IX* pIX = NULL;



// 查询接口IX

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

if (SUCCEEDED(hr))

{

   // 通过接口调用函数

   pIX->Fx();

}

}

QueryInterfaceの実装

指定されたIIDに従って、対応するインターフェイスへのポインタを返します。コンポーネントが顧客によって指定されたインターフェースをサポートしている場合、S_OKと対応するポインターを返す必要があります。戻りテストがサポートされていない場合は、E_NoINTERFACEを返し、対応するポインターの戻り値をNULLに設定します。

QueryInterfaceの実装では、あるタイプを別のタイプの構造にマップできる必要があります。例:if else、配列、ハッシュテーブル、またはツリーを実現しますが、caseステートメントは使用できません。インターフェイス識別子は構造体であり、数値ではないためです。

注:IXおよびIYは、仮想的な方法でIUnknownを継承できません。それ以外の場合、IXおよびIYのvtblの最初の3つの関数は、IUnknownの3つのメンバー関数を指しません。

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

{{

if (iid == IID_IUnknown)

{

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

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

}

else if (iid == IID_IX)

{

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

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

}

else if (iid == IID_IY)

{

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

   *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
インターフェイスの標準化。COMには2つの側面があります。1つはインターフェイスの基本機能の標準化であり、もう1つはインターフェイスのメモリ構造の標準化です。基本機能のコンポーネントインターフェイスの標準化を確実にするために、COMは基本インターフェイスIunknowを事前定義します。(ファイルUNKNWN.Hで定義)

クラスIunknown

{{

Public:

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

    Virtual  HRESULT _stdcal   AddRef( )=0;

    Virtual  HRESULT _stdcal   Release( )=0;

};

明らかに、Iunknownインターフェイスには3つの純粋仮想関数があります。COMでは、コンポーネントのすべてのインターフェイスがこれら3つのサービス(機能)を提供できるように、コンポーネントのすべてのインターフェイスがIUnknownインターフェイスから継承する必要があります。

完全な例
(vs2008)コードのダウンロード:http://www.box.net/shared/m4yr9z73zu

名前空間stdを使用してコード
#include
コピーします;
#include <objbase.h>

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

//インターフェイス定義
インターフェイスIX:IUnknown
{ virtual void __stdcall Fx()= 0; };

インターフェイスIY:IUnknown
{ virtual void __stdcall Fy()= 0; };

インターフェイスIZ:IUnknown
{ virtual void __stdcall Fz()= 0; };

// GUIDの前方参照
externconst IID IID_IX;
extern const IID IID_IY;
extern const IID IID_IZ;

//
//インターフェイスIX、IYを実装します(ここではコンポーネントを意味します)
//
クラスCA:public IX、public IY
{ // IUnknown実装virtualHRESULT __stdcall QueryInterface(const IID&iid、void ** ppv); virtual ULONG __stdcall AddRef( ){ return0 ;}仮想ULONG__stdcall Release(){return 0;}



// Interface IX implementation
virtual void __stdcall Fx() { cout << "这里是Fx函数" << endl;}

// Interface IY implementation
virtual void __stdcall Fy() { cout << "这里是Fy函数" << endl;}

};

HRESULT __stdcall CA :: QueryInterface(const IID&iid、void ** ppv)
{ if(iid == IID_IUnknown){ trace( "QueryInterface:IUnknownへのポインターを返します。"); ppv = static_cast <IX >(this); } else if(iid == IID_IX){ trace( "QueryInterface:IXへのポインタを返します。"); ppv = static_cast <IX >(this); } else if(iid == IID_IY){ trace( "QueryInterface:IYへのポインタを返します。"); ppv = static_cast <IY >(this); } else { trace( "QueryInterface:インターフェイスはサポートされていません。"); ppv = NULL; E_NOINTERFACEを返します。}





















reinterpret_cast <IUnknown
>(* ppv)-> AddRef(); //加计数
returnS_OK;
}

//
//クラスCAを作成し、IUnknownへのポインタを返します
//
IUnknown * CreateInstance()
{ IUnknown * pI = static_cast <IX *>(new CA); pI-> AddRef(); return pI; }



//
//以下は、各インターフェイスのIIDである
//
// {32bb8320-b41b-11CF-a6bb-0080c7b2d682}
静的定数IID IID_IX =
{0x32bb8320、0xb41b、0x11cf、
{は0xA6、0xbb、0x0の、0x80を、0xc7の、コード(0xB2) 、0xd6、0x82}};

// {32bb8321-b41b-11cf-a6bb-0080c7b2d682}
static const IID IID_IY =
{0x32bb8321、0xb41b、0x11cf、
{0xa6、0xbb、0x0、0x80、0xc7、0xb2、0xd6、0x82}};

// {32bb8322-b41b-11cf-a6bb-0080c7b2d682}
static const IID IID_IZ =
{0x32bb8322、0xb41b、0x11cf、
{0xa6、0xbb、0x0、0x80、0xc7、0xb2、0xd6、0x82}};

//
//メイン関数(ここでは顧客に代わって)
//
int main()
{ HRESULT hr;

trace("Client:获取 IUnknown指针.");
IUnknown* pIUnknown = CreateInstance();

trace("Client:获取接口IX.");

IX* pIX = NULL; 
hr = pIUnknown->QueryInterface(IID_IX, (void**)&pIX);
if (SUCCEEDED(hr))
{
    trace("Client:获取接口IX成功.");
    pIX->Fx();          // 使用 IX.
}

trace("Client:获取接口IY.");

IY* pIY = NULL;
hr = pIUnknown->QueryInterface(IID_IY, (void**)&pIY);
if (SUCCEEDED(hr))
{
    trace("Client:         Succeeded getting IY.");
    pIY->Fy();          // 使用 IY.
}


trace("Client:是否支持接口IZ.");

IZ* pIZ = NULL;
hr = pIUnknown->QueryInterface(IID_IZ, (void**)&pIZ);
if (SUCCEEDED(hr))
{
    trace("Client:获取接口IZ成功.");
    pIZ->Fz();
}
else
{
    trace("Client:获取接口IZ失败,不支持接口IZ.");
}


trace("Client:用接口IX查询接口IY.");

IY* pIYfromIX = NULL;
hr = pIX->QueryInterface(IID_IY, (void**)&pIYfromIX);
if (SUCCEEDED(hr))
{    
    trace("Client:获取接口IY成功.");
    pIYfromIX->Fy();
}


trace("Client:用接口IY查询接口IUnknown.");

IUnknown* pIUnknownFromIY = NULL;
hr = pIY->QueryInterface(IID_IUnknown, (void**)&pIUnknownFromIY);
if (SUCCEEDED(hr))
{
    cout << "IUnknown指针是否相等?";
    if (pIUnknownFromIY == pIUnknown)
    {
        cout << "Yes, pIUnknownFromIY == pIUnknown." << endl;
    }
    else
    {
        cout << "No, pIUnknownFromIY != pIUnknown." << endl;
    }
}

// Delete the component.
delete pIUnknown;

return 0;

}
コードをコピーする


QueryInterfaceの実装ルールQueryInterfaceは、常に同じIUnknownポインターを返します。

お客様がインターフェースを取得したことがある場合は、いつでもこのインターフェースを入手できます。あなたができなかった場合、あなたは常にすることができなくなります。

顧客は、すでに所有しているインターフェースを再び入手できます。

顧客はインターフェースに戻ることができます。

特定のインターフェイスを特定のインターフェイスから取得できる場合、このインターフェイスは任意のインターフェイスから取得できます。

同じIUnknownポインター:

コンポーネントのインスタンスには、IUnknownインターフェイスが1つだけあります。コンポーネントインスタンスのIUnknownインターフェイスをクエリする場合、どのインターフェイスが渡されても、結果は同じポインタ値になるためです。したがって、2つのインターフェイスのIUnknownを渡して、それらの値を比較できます。それらが同じであるかどうかを確認して、2つのインターフェースが同じコンポーネントにあるかどうかを判別します。

/ *

判断两个接口是否在同一个组件里

* /

BOOL SameComponents(IX * pIX、IY * pIY)

{{

IUnknown* pI1 = NULL;

IUnknown* pI2 = NULL;



pIX->QueryInterface(IID_IUnknown, (void**)&pI1);

pIY->QueryInterface(IID_IUnknown, (void**)&pI2);

return pI1 == pI2;

}

QueryInterfaceはコンポーネントを定義します。
コンポーネントは実際にはQueryInterfaceによって定義されます。コンポーネントでサポートされているインターフェイスのセットは、QueryInterfaceがインターフェイスポインタを返すことができるインターフェイスです。これは、実装チームのC ++クラスではなく、QueryInterfaceの実装によって決定されます。コンポーネントを実装するクラスの継承階層も、コンポーネントを判別できません。

新しいバージョンのコンポーネントの
COMインターフェイスは変更されません。コンポーネントがインターフェイスを解放し、特定の用途を補完する場合、このインターフェイスは変更されません。変更したい場合。新しいインターフェースを追加することによってのみ。

新しいバージョンを作成
する必要がある場合次の条件のいずれかが変更された場合、新しいIDを新しいインターフェイスに割り当てる必要があります。

インターフェイスの機能の数。

インターフェイスの関数の順序。

関数のパラメーター。

関数のパラメーターの順序。

関数パラメーターのタイプ。

関数の可能な戻り値。

関数の戻り値のタイプ。

関数パラメーターの意味。

インターフェイスの関数の意味。

つまり、変更が顧客の通常の操作に影響を与える限り、新しいIDをインターフェイスに割り当てる必要があります

この記事のアドレス:http//www.cnblogs.com/fangyukuan/archive/2010/06/02/1750377.html

おすすめ

転載: blog.csdn.net/yunxiang1224/article/details/88654768