Microsoft DirectX 8.0

DLL の作成方法

ここでは、Microsoft® DirectShow® でコンポーネントをダイナミック リンク ライブラリ (DLL) として実装する方法を説明する。このトピックは、CUnknown 基底クラスからコンポーネントを派生することによって IUnknown インターフェイスを実装する方法を説明した「IUnknown の実装方法」の続きである。

このトピックは、以下のセクションを含んでいる。

(汎用 COM オブジェクトとは異なり) DirectShow フィルタの登録には、ここで説明していないいくつかの追加のステップが必要になる。フィルタの登録については、「DirectShow オブジェクトの登録方法」を参照すること。

クラス ファクトリとファクトリ テンプレート

クライアントは COM オブジェクトのインスタンスを作成する前に、CoGetClassObject 関数呼び出しを使用してオブジェクトのクラス ファクトリのインスタンスを作成する。次にクライアントは、クラス ファクトリの IClassFactory::CreateInstance メソッドを呼び出す。実際にコンポーネントを作成したり、要求されたインターフェイスへのポインタを返すのはクラス ファクトリである。(CoCreateInstance 関数は、これらのステップを関数呼び出し内で組み合わせる。)

次の図に、メソッド呼び出しのシーケンスを示す。

CoGetClassObject は、DLL で定義された DllGetClassObject 関数を呼び出す。この関数はクラス ファクトリを作成し、クラス ファクトリ上のインターフェイスへのポインタを返す。DirectShow は DllGetClassObject を実装するが、この関数は特定の部分でコードに依存する。この動作を理解するためには、DirectShow がどのようにクラス ファクトリを実装するかを理解することが必要である。

クラス ファクトリは、別の COM オブジェクトを作成する処理に特化した COM オブジェクトである。それぞれのクラス ファクトリでは、作成するオブジェクトの型が決まっている。DirectShow では、すべてのクラス ファクトリは同じ C++ クラス CClassFactory のインスタンスである。クラス ファクトリは、ファクトリ テンプレートと呼ばれるクラス CFactoryTemplate によって特化される。各クラス ファクトリは、ファクトリ テンプレートへのポインタを保持する。ファクトリ テンプレートには、特定のコンポーネントに関する情報 (コンポーネントのクラス識別子 (CLSID) など) と、そのコンポーネントを作成した関数へのポインタが格納される。

DLL は、ファクトリ テンプレートのグローバル配列を DLL 内の各コンポーネントに 1 つずつ宣言する。新しいクラス ファクトリを作成するとき、DllGetClassObject は同じ CLSID を持つテンプレートの配列を探す。該当するものが見つかると、DllGetClassObject は当該テンプレートへのポインタを保持するクラス ファクトリを作成する。クライアントが IClassFactory::CreateInstance を呼び出すと、クラス ファクトリはテンプレートに定義されているインスタンス化関数を呼び出す。

次の図に、メソッド呼び出しのシーケンスを示す。

このアーキテクチャの利点は、クラス ファクトリ全体を実装することなく、インスタンス化関数などのコンポーネントに固有のいくつかの要素だけを定義すれば良いということである。

ファクトリ テンプレートの配列

ファクトリ テンプレートは、以下のパブリック メンバ変数を格納する。

const WCHAR *              m_Name;                // 名前
const CLSID *              m_ClsID;               // CLSID
LPFNNewCOMObject           m_lpfnNew;             // コンポーネントのインスタンス
                                                  //   を作成する関数
LPFNInitRoutine            m_lpfnInit;            // 初期化関数 (オプション)
const AMOVIESETUP_FILTER * m_pAMovieSetup_Filter; // セットアップ情報 (フィルタ用)

2 つの関数ポインタ m_lpfnNew および m_lpfnInit は、以下の型定義を使用する。

typedef CUnknown *(CALLBACK *LPFNNewCOMObject)(LPUNKNOWN pUnkOuter, HRESULT *phr);
typedef void (CALLBACK *LPFNInitRoutine)(BOOL bLoading, const CLSID *rclsid);

1 番目の定義は、コンポーネントのインスタンス化関数である。2 番目の定義は、オプションの初期化関数である。初期化関数を指定した場合、関数は DLL エントリ ポイント関数内部から呼び出される。(DLL エントリ ポイント関数については、後の節で説明する。)

たとえば CUnknown から継承した CMyComponent という名前のコンポーネントを含む DLL を作成するとする。この場合、以下の項目を DLL に提供しなければならない。

これらの項目を宣言する例を以下に示す。

// 新しいインスタンスを返すプライベート メソッド
CUnknown * WINAPI CMyComponent::CreateInstance(LPUNKNOWN pUnk, HRESULT *pHr) {
    CMyComponent *pNewObject = new CMyComponent(NAME("My Component"), pUnk, pHr );
if (pNewObject == NULL) {
*phr = E_OUTOFMEMORY;
    }
return pNewObject;
} 

CFactoryTemplate g_Templates[1] = 
{
    { 
      L"My Component",      // 名前
      &CLSID_MyComponent,         // CLSID
      CMyComponent::CreateInstance,   // MyComponent のインスタンスを作成するメソッド
      NULL,                           // 初期化関数
      NULL                            // セットアップ情報 (フィルタ用)
    }
};
int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]);	

CreateInstance メソッドは、クラス コンストラクタを呼び出して新しいクラス インスタンスへのポインタを返す。パラメータ pUnk は、集成化 IUnknown へのポインタである。このパラメータをクラス コンストラクタに渡す。パラメータ pHr は、HRESULT 値へのポインタである。クラス コンストラクタはこれを適切な値に設定する。ただしコンストラクタが失敗した場合は、値を E_OUTOFMEMORY に設定する。

NAME マクロは、デバッグ ビルドにおいて文字列を生成するが、リテール ビルドでは NULL に解決する。この例でコンポーネントに与えられている名前は、デバッグ ログには記録されるが、最終バージョンではメモリを占有しない。

CreateInstance はプライベート メソッドなので任意の名前を持つことができる。クラス ファクトリは CreateInstance へのポインタをファクトリ テンプレート内で受け取る。ただし、g_Templatesg_cTemplates は、クラス ファクトリが期待するグローバル変数なので、必ずこの名前を持たなければならない。

DLL 関数

DLL の登録、登録解除、メモリへのロードを可能にするためには、DLL は以下の関数を実装しなければならない。

これらのうち、最初の 3 つは DirectShow によって実装される。ファクトリ テンプレートが初期化関数を m_lpfnInit メンバ変数で提供する場合、その関数は DLL エントリ ポイント関数内部から呼び出される。システムが DLL エントリ ポイント関数を呼び出すタイミングについては、Platform SDK の DllMain を参照すること。

DllRegisterServerDllUnregisterServer は自分で実装しなければならない。しかし、DirectShow には必要な操作を行うための AMovieDllRegisterServer2 関数が提供されている。次の例に示すように、単にこの関数をコンポーネントにラップすれば良い。

STDAPI DllRegisterServer()
{
    return AMovieDllRegisterServer2( TRUE );
}

STDAPI DllUnregisterServer()
{
    return AMovieDllRegisterServer2( FALSE );
}

ただし、DllRegisterServer および DllUnregisterServer 内では、登録プロセスを必要に応じてカスタマイズすることができる。DLL にフィルタが含まれているなら、いくつかの追加作業が必要である。詳細については、「DirectShow オブジェクトの登録方法」を参照すること。

モジュール定義 (.def) ファイル内で、エントリ ポイント関数以外のすべての DLL 関数をエクスポートする。以下に、.def ファイルの例を示す。

EXPORTS
    DllGetClassObject PRIVATE
    DllCanUnloadNow PRIVATE
    DllRegisterServer PRIVATE
    DllUnregisterServer PRIVATE

DLL は Regsvr32.exe ユーティリティを使って登録することができる。