COMサーバとレジストリ
CoCreateInstance()が呼び出されたとき、システムはレジストリを参照してCOMサーバを作成します。
その登録先は次のようになっています。
レジストリキー | インストール権限 | 内容 |
HKEY_LOCAL_MACHINE\SOFTWARE\Classes | Administrator | マシン全体のCOMサーバ |
HKEY_CURRENT_USER\SOFTWARE\Classes | 現在のユーザ | 現在のユーザ限定のCOMサーバ |
Classes以下に登録される情報の一部を以下にあげます。
サブキー | 値の名前 | 値 |
CLSID\{xxxxxxxx-...xx}\InprocServer32 | なし | DLLサーバのフルパス |
CLSID\{xxxxxxxx-...xx}\InprocServer32 | ThreadingModel | スレッドモデル。なければメインシングルスレッド限定 |
CLSID\{xxxxxxxx-...xx}\LocalServer32 | なし | EXEサーバのフルパス |
CLSID\{xxxxxxxx-...xx} | InprocHandler32 | インターフェース内のメソッド呼び出しを仲介するDLLサーバのフルパス |
CLSID\{xxxxxxxx-...xx} | ProgID | プログラムIDの文字列 "xxxxx.x.x" |
CLSID\{xxxxxxxx-...xx} | AppID | AppIDを識別するGUID |
Interface\{xxxxxxxx-...xx} | ProxyStubClsid32 | インターフェース内のメソッド呼び出しを仲介するDLLサーバのCLSID |
"xxxxx.x.x" | CLSID | ProgID(文字列)に関連付けられたCLSID |
AppID\{xxxxxxxx-...xx} | DllSurrogate | DLLをEXEサーバとして読み込むための実行ファイルへのフルパス |
これらのうち、必要なキーを登録していくことになります。
レジストリへの登録方法
COMサーバが呼び出されるためには、予めCOMサーバをレジストリに登録しておく必要があります。
レジストリへの登録には、次のいずれかの方法があります。
インストーラを作成し、必要な情報をレジストリに登録
DLLサーバであれば、自己登録関数を実装し、regsvr32ツールにより登録
EXEサーバであれば、コマンドラインでの自己登録スイッチを実装
CIchigo1については、DLLの自己登録関数の形で実装することにします。
DLLの自己登録関数
DLLの自己登録関数は3種類あり、その機能は次のようになっています。
DLLの関数 | 引数 | 機能 |
DllRegisterServer | なし | マシン全体のCOMサーバとして登録する。 |
DllUnregisterServer | なし | マシン全体のCOMサーバとしての登録を解除する。 |
DllInstall | 登録モード,文字列 | 引数にしたがったCOMサーバの登録か登録解除を行う。 |
DllInstallの実装
DllInstallは次の形をしています。
EXTERN_C HRESULT STDAPICALLTYPE DllInstall(
BOOL bInstall, LPCWSTR pwszCmdline );
bInstallがTRUEであれば登録、FALSEであれば登録解除します。
pwszCmdlineには任意の文字列を与えることが可能ですが、マイクロソフトの実装では、
引数としてuserが与えられていれば登録先を現在のユーザ限定に切り替える動作をするようです。
マイクロソフトの実装では、引数の大文字小文字は区別されません。
CIchigo1に対して、この動作をするようにDllInstall1()を実装した例を次に示します。
HINSTANCE g_hinstDLL = NULL;
EXTERN_C BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD dwReason,
LPVOID pvReserved )
{
if ( dwReason == DLL_PROCESS_ATTACH ) {
g_hinstDLL = hinstDLL;
}
return TRUE;
}
EXTERN_C HRESULT STDAPICALLTYPE DllInstall( BOOL bInstall,
LPCWSTR pwszCmdline )
{
const bool per_user_flag = suppIsPerUserRequest( pwszCmdline );
try {
HKEY hkeyRoot;
std::wstring wstrSubkey;
LONG regerr;
if ( bInstall ) {
std::wstring wstrName = suppGetModuleFileName( g_hinstDLL );
wstrSubkey = suppGetRegKeyCLSID(
&hkeyRoot, per_user_flag, CLSID_Ichigo1,
L"InprocServer32" );
if ( wstrSubkey.empty() || wstrName.empty() ) {
return E_FAIL;
}
regerr = suppRegSetStringW(hkeyRoot, wstrSubkey.c_str(), NULL,
wstrName.c_str() );
if ( regerr != ERROR_SUCCESS ) {
return SELFREG_E_CLASS;
}
} else {
wstrSubkey = suppGetRegKeyCLSID(
&hkeyRoot, per_user_flag, CLSID_Ichigo1,
NULL );
if ( wstrSubkey.empty() ) {
return E_FAIL;
}
regerr = suppRegRemoveKeyW(hkeyRoot, wstrSubkey.c_str());
if ( regerr != ERROR_SUCCESS ) {
return SELFREG_E_CLASS;
}
}
} catch ( const std::exception& ) {
return E_FAIL;
}
return S_OK;
}
DLLのファイル名を取得するために、予めDllMainでDLLのハンドルを保存しています。
DllInstall()では、はじめに引数にuserがあるかを調べ、per_user_flagに保存しています。
登録モードであればCLSIDに対応したキーとInprocServer32キーを作成してDLLへのフルパスに保存し、
登録解除モードであればCLSIDに対応したキーを削除しています。
DLLの外部から呼び出される関数やメソッドでは、
C++のライブラリが発生する例外をDLL自身でキャッチする必要があります。
この例では、DllInstall()の直後でtry、catchを用いて例外をキャッチしています。
suppではじまるヘルパ関数は、主にレジストリを操作するものです。
説明を簡潔にするために、別ページでまとめています。
サンプルで用いたヘルパ関数をご覧ください。
残りの関数の実装
DllInstall()の実装が完了していれば、残りの関数はDllInstall()を呼び出す形で実装できます。
EXTERN_C HRESULT STDAPICALLTYPE DllRegisterServer()
{
return DllInstall( TRUE, NULL );
}
EXTERN_C HRESULT STDAPICALLTYPE DllUnregisterServer()
{
return DllInstall( FALSE, NULL );
}
関連ページ
COM/ActiveXの解説ページ 目次
前の項目: DLL利用状態の管理
次の項目: DLLサーバのビルド