IUnknown の動作
IUnknown のメソッドを使うと、アプリケーションはコンポーネントのインターフェイスを問い合わせたり、コンポーネントの参照カウントを管理できる。
参照カウント
参照カウントは、AddRef メソッドでインクリメントされ Release メソッドでデクリメントされる内部変数である。基底クラスは、参照カウントを管理し、複数のスレッド間で参照カウントへのアクセスを同期する。
インターフェイスの問い合わせ
インターフェイスの問い合わせはわかりやすい。呼び出し元は、インターフェイス識別子 (IID) とポインタのアドレスの 2 つのパラメータを渡す。要求されたインターフェイスをコンポーネントがサポートしている場合、コンポーネントはそのインターフェイスへのポインタを設定し、専用の参照カウントをインクリメントし、S_OK を返す。コンポーネントがそのインターフェイスをサポートしていない場合、コンポーネントは NULL へのポインタを設定し、E_NOINTERFACE を返す。次の擬似コードは、QueryInterface メソッドの概要を示す。次のセクションで説明するコンポーネントの集成化では、処理が複雑になっている。
if (IID == IID_IUnknown)
(IUnknown *)this へのポインタを設定する。
AddRef
return S_OK
else if (IID == IID_ISomeInterface)
(ISomeInterface *)this へのポインタを設定する。
AddRef
return S_OK
else if ...
else
NULL へのポインタを設定する。
return E_NOINTERFACE
1 つのコンポーネントの QueryInterface メソッドと別のコンポーネントの QueryInterface メソッドの唯一の違いは、それぞれのコンポーネントがテストする IID のリストにある。コンポーネントがサポートするすべてのインターフェイスに対して、コンポーネントはそのインターフェイスの IID のテストを行わなければならない。
集成化と委任
コンポーネントの集成は呼び出し元に対して透過的でなければならない。したがって、集成は、外部コンポーネントの実装に従う集成されたコンポーネントを使って、1 つの IUnknown インターフェイスを公開しなければならない。そうでないと、呼び出し元は同じ集成内で 2 つの異なる IUnknown インターフェイスを認識することになる。コンポーネントが集成されていない場合、コンポーネントは独自の実装を使う。
この動作をサポートするために、コンポーネントは間接的なレベルを追加しなければならない。"委任 IUnknown" は、作業を適切な場所に委任する。すなわち、インターフェイスが 1 つであれば外部コンポーネントか、またはコンポーネントの内部バージョンに委任する。"非委任 IUnknown" は、前のセクションで説明した操作を行う。
委任バージョンはパブリックで、IUnknown の名前をそのまま保持する。非委任バージョンは INonDelegatingUnknown に名前が変更される。これはパブリック インターフェイスではないので、この名前は COM 仕様の一部ではない。
クライアントがコンポーネントのインスタンスを作成するとき、クライアントは IClassFactory::CreateInstance メソッドを呼び出す。パラメータは、集成コンポーネントの IUnknown インターフェイスへのポインタか、または新しいインスタンスが集成されていない場合は NULL である。次の例に示すように、コンポーネントは使用する IUnknown インターフェイスを示すメンバ変数を格納するためにこのパラメータを使う。
CMyComponent::CMyComponent(IUnknown *pOuterUnkown)
{
if (pOuterUnknown == NULL)
m_pUnknown = (IUnknown *)(INonDelegatingUnknown *)this;
else
m_pUnknown = pOuterUnknown;
[ ... コンストラクタのコードが続く ... ]
}
委任 IUnknown の各メソッドは、次の例に示すように対応する非委任メソッドを呼び出す。
HRESULT QueryInterface(REFIID iid, void **ppv)
{
return m_pUnknown->QueryInterface(iid, ppv);
}
委任動作の本質上、委任メソッドはすべてのコンポーネントにおいて同じに見える。非委任バージョンだけが変わる。