私らしくピンポイントな話題で言ってみたいと思います。
と言うわけで、こういう話です。使い方としては
- DirectShowのフィルタDLLがあるときに、そのDLLを直接使用してフィルタを使用する
- 「regsvr32で登録しなければ使用できない」と書いてはあるが、システムに登録すると問題が起こるのでそれを回避する
と言ったときにこんなやり方をとります。
つまるところ、自分でCoCreateInstanceをシミュレートするわけですね。
サンプルコード
こんな感じです。IObjectは作成対象となるインターフェイスの型に置き換えてください。
また、エラー処理はすべて省いています。
//DllGetClassObjectの関数型定義 typedef HRESULT (STDAPICALLTYPE *LPFNGETCLASSOBJECT)(REFCLSID rclsid,REFIID riid,LPVOID FAR* ppv); LPFNGETCLASSOBJECT fnGetClassObject; IClassFactory *lpClassFactory; HMODULE hModule; HRESULT hr; IObject *lpObject; //作成対象のCLSIDをCLSID_ClassID、IIDをIID_ClassID、DLLファイル名をlibsvr.dllとする //CoCreateInstanceを使う場合は //hr = CoCreateInstance(CLSID_ClassID,NULL,CLSCTX_INPROC_SERVER,IID_ClassID,(void **)&lpObject); //と書くべきところを・・・ //1. regsvr32ができるDLLをLoadLibraryでロード hModule = LoadLibrary(TEXT("libsvr.dll")); //2. DllGetClassObjectを取得 fnGetClassObject = (LPFNGETCLASSOBJECT)GetProcAddress(hModule,"DllGetClassObject"); //3. CLSIDを指定してIClassFactoryを取得 hr = fnGetClassObject(CLSID_ClassID,IID_IClassFactory,(void **)&lpClassFactory); //4. IIDで指定したインターフェイスを作成 hr = lpClassFactory->CreateInstance(NULL,IID_ClassID,(void **)&lpObject); lpClassFactory->Release(); //5. 作成されたオブジェクトを使用 //6. 解放 lpObject->Release(); //7. ロードされていたDLLを解放 FreeLibrary(hModule);
CoCreateInstance(+CoGetClassObject)の内部動作をまねるとこんな感じ
です。COMが分かっている人ならさほど難しくないと思います。
もちろん、いくつか注意点があります。
この方法でライブラリをロードするときはCoInitalizeおよびCoUninitializeはほとんどの場合必要
まあ、実はと言うと必ずしも必要というわけではないです。インターフェイスの状態によってはCoInitializeにより初期化していなくても作成できます。
しかし、作成したインターフェイスに付属しているオブジェクトがCoCreateInstanceを必要とするなら作成に失敗する、と言うことが起こります。
そのため、このサンプルコードの前後にCoInitializeやCoUninitializeがないとちょっと危ない事態になります。
(DirectDrawなんかのDirectX系ライブラリがCOMとして動いている(ように見える)のにCoInitailizeなしでも大丈夫という訳のはこういう理由だったりします)
DLLの解放は対象オブジェクトがすべて解放されていることが確定している時点でのみ可能
まあ、当たり前ですね。その前に解放するとDLLのコードがプロセスメモリ上から失われてしまい、エラーになってしまいます。
なので、FreeLibraryを行うならプログラムを終了する直前や、CoUninitializeを書いているポイントで行うと良いでしょう。
つまるところ、本来CoUninitialize関数が内部的にやっていることを自前でやれ、と言うわけですね。
最悪、DllCanUnloadNow関数を取得してFreeLibraryが出来るかどうか確認する必要がありますが。
CoCreateInstanceの中身が分かったのでちょっとおもしろかった
と言うネタでした。CLSIDとIIDが内部的には別のポイントで使用されていたり、作成のためにIClassFactoryが中間に出てきたりと。
なお、DllGetClassObject関数の実装例についてはDirectShowのライブラリ(bassclasses)にあるdllentry.cppを見ると良いと思います。
分かったところでどこで使うの?とか言われるとちょっと厳しいですが・・・。
追記(12/07) regsvr32の使い方、COMオブジェクト一覧の取得
どうもregsvr32の名前を出したためにこの検索条件で来ることが多いような気がします。
ということで、これについて書いておきます。
まず、regsvr32の使い方ですが、regsvr32で登録できるDLLは
- DllGetClassObjectが実装されている
- DllRegisterServer、DllUnregisterServerが実装されている
- (opt)DllCanUnloadNowが実装されている
必要があります。これがないと登録は出来ません。
で、登録/登録解除の手順としては、D:\foo\bar.dllを登録するとき、
- 管理者権限でコマンドプロンプトを起動
- プロンプト上で以下のコマンドを実行
<登録時>
regsvr32 "D:\foo\bar.dll"<登録解除時>
regsvr32 /u "D:\foo\bar.dll"
となります。登録するとDLLは移動できなくなるので置く場所に注意が必要です。
COMオブジェクト一覧を取得するのには「OLE-COM Object Viewer」というプログラムを使用します。
Windows SDKに同梱されているのでダウンロードしてインストールすれば使用できるようになります。