さて、必要な機能は全て実装したのだが、
一般的なインプロセスサーバとして完成させるには、
あともう少しやるべきことがある。

インプロセスサーバは、DLL をコピーするだけでは動作せず、
サーバに含まれる COM クラスを公開するために、
レジストリに登録する必要があった。

レジストリの登録情報は、DLL のパスに依存しているため、
インプロセスサーバをシステムにインストールする際に、
レジストリファイルを相手に配るのは現実的ではない。

そのため、COM サーバには、登録・登録解除を
自分自身で行うための仕組みが存在する。

インプロセスサーバの場合は、DllRegisterServer と、
DllUnregisterServer の 2 つの関数を実装し、
それぞれで自身の登録と登録解除処理を行う必要がある。

    STDAPI DllRegisterServer();
    STDAPI DllUnregisterServer();

通常は自身の公開する COM クラスの登録のみを行うが、
DLL が必要とする独自機能のための追加登録や、
ファイルのコピーなどを行っても構わない。

LinkIconOverlays.dll の場合は、
COM クラスとシェル拡張の登録を行うことになる。

レジストリの登録には、Reg~ 系の API を使うのだが、
これら API を使って値をレジストリにアクセスし、
値を登録するためには、意外と手間が掛かる。

そこで、まずはレジストリ用のヘルパ関数を用意しよう。

レジストリに値を登録するためには、
キーを開く⇒値を登録⇒キーを閉じるという工程が必要だ。

値を登録した時点でエラーが発生した場合でも、
開いたキーは閉じる必要があるので、
これを 1 つの関数内に書いていくと見通しが悪くなる。

そこでこれらをまとめて実行する関数を用意し、
例外(HRESULT)処理を書きやすくしておこう。

まあ、C++ の機能を生かしてクラス化したほうが良いのだが、
今回はあえて関数指向で書くことにした。

========== library.cpp ==========

// (…前略…)
// (…DllMain…)
// (…DllGetClassObject…)
// (…DllCanUnloadNow…)

// レジストリに値を登録する
static LONG WriteRegistryStringValue(
        HKEY baseKey,
        LPCTSTR path,
        LPCTSTR name,
        LPCTSTR value) {

    HKEY key;

    // キーを開くか作成
    LONG error = RegCreateKeyEx(baseKey, path,
            0, NULL, 0, KEY_WRITE, NULL, &key, NULL);

    if (error != ERROR_SUCCESS) return error;

    if (value != NULL) {

        // 値を登録
        error = RegSetValueEx(key, name, 0, REG_SZ,
                reinterpret_cast<const BYTE*>(value),
                (lstrlen(value) + 1) * sizeof (TCHAR));

    }

    // キーを閉じる
    RegCloseKey(key);

    return error;
}

========== (library.cpp) ==========

まずは WriteRegistryStringValue というヘルパ関数だ。
これは、library.cpp 内でしか使わない関数なので、
先頭に static を付与し、内部リンケージとしておく。

RegCreateKeyEx は、レジストリキーを開くか作成する。
引数は多いが、ほとんどはお決まりの値だ。

baseKey は基準となるキーであり、
通常は HKEY_CLASSES_ROOT などの値である。
これは HKEY 型の定数として定義されている。

開いたキーは引数で受け取る事になっており、
戻り値は Windows のエラーコードとなる。

~Ex という関数は、Ex が付かない関数を拡張したものだ。
Windows API にはこの手の名前が良く登場する。
別に古い RegCreateKey を使っても良いのだが、
大抵は 16 ビット時代の賜物で、
昨今の環境では非推奨になっていることが多い。

RegSetValueEx は、開いたキーに値を登録する。

今回の登録情報は全て文字列値なので、
REG_SZ というレジストリ型を指定している。

レジストリには無名の値が存在するため、
name は NULL でも構わないのだが、
値が NULL になるのは認められない。

レジストリには値として文字列以外も
バイナリや整数型も登録できるので、
値は BYTE 配列として受け取ることになっている。

そこで文字列を強制的に BYTE* 型にキャストしている。
データの長さもバイト単位なので、
末尾の NUL も含めて計算が必要となる。

書き込みが終われば、RegCloseKey でキーを閉じる。

========== library.cpp ==========

// レジストリの値を削除する
static LONG DeleteRegistryValue(
        HKEY baseKey,
        LPCTSTR path,
        LPCTSTR name) {

    HKEY key;

    // キーを開く
    LONG error = RegOpenKeyEx(baseKey, path, 0, KEY_WRITE, &key);
    if (error != ERROR_SUCCESS) return error;

    // 値を削除
    error = RegDeleteValue(key, name);

    // キーを閉じる
    RegCloseKey(key);

    return error;
}

========== end of library.cpp ==========

そして、DeleteRegistryValue というヘルパ関数を作る。
これも先頭に static を付与し、内部リンケージとしておく。

RegOpenKeyEx は、RegCreateKeyEx と違い
キーが存在しない場合は作成せずにエラーとなる。

RegDeleteValue で登録されている値を削除し、
RegCloseKey でキーを閉じれば OK だ。

これら 2 つのヘルパ関数を使えば、
キーの開閉を気にせずにコードを書くことができる。