CUnknown の使用
DirectShow は CUnknown と呼ばれる基底クラスの IUnknown を実装する。この CUnknown を使って他のクラスを派生し、コンポーネント間で変わるメソッドだけをオーバーライドできる。DirectShow の他の基底クラスのほとんどは CUnknown から派生するので、作成するコンポーネントを CUnknown または他の基底クラスから直接継承できる。
INonDelegatingUnknown
CUnknown は INonDelegatingUnknown を実装する。CUnknown は参照カウントを内部的に管理し、ほとんどの状況で派生クラスは同じ 2 つの参照カウント メソッドを実装する。CUnknown は参照カウントがゼロになるとそれ自体を削除する。その一方では、CUnknown::NonDelegatingQueryInterface をオーバーライドしなければならない。なぜなら、基底クラスのメソッドは IID_IUnknown 以外の IID を受け取ると E_NOINTERFACE を返すからである。派生クラスでは、次の例に示すようにサポートするインターフェイスの IID をテストする必要がある。
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
if (riid == IID_ISomeInterface)
{
return GetInterface((ISomeInterface*)this, ppv);
}
// デフォルト。
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}
ユーティリティ関数 GetInterface (「COM ヘルパー関数」を参照すること) はポインタを設定し、スレッドに対応した方法で参照カウントを実装し、S_OK を返す。デフォルトのケースでは、基底クラス メソッドを呼び出してその結果を返す。別の基底クラスから派生させた場合は、その NonDelegatingQueryInterface メソッドを代わりに呼び出す。これにより、親クラスがサポートするすべてのインターフェイスをサポートできる。
IUnknown
上述のように、IUnknown の委任バージョンは、非委任バージョンの正しいインスタンスを起動するだけなので、すべてのコンポーネントで同じである。便宜上、ヘッダー ファイル Combase.h は、インライン メソッドとして 3 つの委任メソッドを宣言するマクロ DECLARE_IUNKNOWN を含んでいる。このコードを次に示す。
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) {
return GetOwner()->QueryInterface(riid,ppv);
};
STDMETHODIMP_(ULONG) AddRef() {
return GetOwner()->AddRef();
};
STDMETHODIMP_(ULONG) Release() {
return GetOwner()->Release();
};
ユーティリティ関数 CUnknown::GetOwner は、このコンポーネントを所有するコンポーネントの IUnknown インターフェイスへのポインタを取得する。集成したコンポーネントの場合、その所有者は外部コンポーネントである。それ以外の場合、コンポーネントはそれ自体を所有する。クラス定義の public セクションに DECLARE_IUNKNOWN マクロをインクルードする。
クラス コンストラクタ
クラスに固有の操作に加えて、クラス コンストラクタは、親クラスのコンストラクタ メソッドを起動しなければならない。次の例に、一般的なコンストラクタ メソッドを示す。
CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr)
: CUnknown(tszName, pUnk, phr)
{
/* 他の初期化。 */
};
このメソッドは次のパラメータを取って CUnknown コンストラクタ メソッドに直接渡す。
- tszName は、コンポーネントの名前を指定する。
- pUnk は、集成化 IUnknown へのポインタである。
- pHr は、メソッドの成功または失敗を表す HRESULT 値へのポインタである。
まとめ
次に、IUnknown と ISomeInterface という名前の仮想のインターフェイスをサポートする派生クラスの例を示す。
class CMyComponent : public CUnknown, public ISomeInterface
{
public:
DECLARE_IUNKNOWN;
STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void **ppv)
{
if( riid == IID_ISomeInterface )
{
return GetInterface((ISomeInterface*)this, ppv);
}
return CUnknown::NonDelegatingQueryInterface(riid, ppv);
}
CMyComponent(TCHAR *tszName, LPUNKNOWN pUnk, HRESULT *phr)
: CUnknown(tszName, pUnk, phr)
{
/* 他の初期化。 */
};
// 後でさらに宣言を追加する。
};
この例では、次のポイントを示している。
- CUnknown クラスは IUnknown インターフェイスを実装する。新しいコンポーネントは CUnknown およびコンポーネントがサポートするすべてのインターフェイスを継承する。コンポーネントは、CUnknown から継承した別の基底クラスから派生できる。
- DECLARE_IUNKNOWN マクロは、委任 IUnknown メソッドをインライン メソッドとして宣言できる。
- CUnknown クラスは、INonDelegatingUnknown の実装を提供する。
- IUnknown 以外のインターフェイスを実装するためには、派生クラスは NonDelegatingQueryInterface メソッドをオーバーライドし、新しいインターフェイスの IID のテストを行う必要がある。
- クラス コンストラクタは、CUnknown のコンストラクタ メソッドを起動する。
フィルタ開発における次のステップでは、アプリケーションを有効にしてコンポーネントの新しいインスタンスを作成する。このためには、DLL に関する知識に加え、クラス ファクトリおよびクラス コンストラクタ メソッドとの関係を理解していることが必要になる。詳細については、「DLL の作成方法」を参照すること。