クライアントとコンポーネント間の相互作用は、インターフェースを介して完了します。顧客がコンポーネントの他のインターフェースを照会するとき、それはインターフェースを介して行われます。このインターフェイスは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