ゼロからのCOM - DLLサーバの自己登録

いちごパック > COM/ActiveXの解説 > ゼロからのCOM - DLLサーバの自己登録

COMサーバとレジストリ

CoCreateInstance()が呼び出されたとき、システムはレジストリを参照してCOMサーバを作成します。 その登録先は次のようになっています。
レジストリキーインストール権限内容
HKEY_LOCAL_MACHINE\SOFTWARE\ClassesAdministratorマシン全体のCOMサーバ
HKEY_CURRENT_USER\SOFTWARE\Classes現在のユーザ現在のユーザ限定のCOMサーバ
Classes以下に登録される情報の一部を以下にあげます。
サブキー値の名前
CLSID\{xxxxxxxx-...xx}\InprocServer32なしDLLサーバのフルパス
CLSID\{xxxxxxxx-...xx}\InprocServer32ThreadingModelスレッドモデル。なければメインシングルスレッド限定
CLSID\{xxxxxxxx-...xx}\LocalServer32なしEXEサーバのフルパス
CLSID\{xxxxxxxx-...xx}InprocHandler32インターフェース内のメソッド呼び出しを仲介するDLLサーバのフルパス
CLSID\{xxxxxxxx-...xx}ProgIDプログラムIDの文字列 "xxxxx.x.x"
CLSID\{xxxxxxxx-...xx}AppIDAppIDを識別するGUID
Interface\{xxxxxxxx-...xx}ProxyStubClsid32インターフェース内のメソッド呼び出しを仲介するDLLサーバのCLSID
"xxxxx.x.x"CLSIDProgID(文字列)に関連付けられたCLSID
AppID\{xxxxxxxx-...xx}DllSurrogateDLLを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サーバのビルド