昨日作った WriteRegistryStringValue を使うと、
COM クラスとエクステンションの登録処理を、
少しは見やすく書くことができる。

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

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

// COM クラスと重ね合わせアイコンハンドラの登録
static HRESULT RegisterIconOverlayHandler(
        REFCLSID classID,
        LPCTSTR description,
        LPCTSTR threadingModel) {

    // 軽いチェック
    if (description == NULL) return E_UNEXPECTED;
    if (threadingModel == NULL) return E_UNEXPECTED;

    // CLSID を文字列に
    WCHAR clsidString[40];
    if (!StringFromGUID2(classID, clsidString, 40)) return SELFREG_E_CLASS;

    WCHAR keyPath[256];

    // DLL のパスを得る
    TCHAR modulePath[MAX_PATH];
    if (!GetModuleFileName(g_moduleHandle, modulePath, MAX_PATH))
            return E_UNEXPECTED;

    LONG error = ERROR_SUCCESS;

    // CLSID\<クラス ID> のパスを作成
    wsprintf(keyPath, L"CLSID\\%s", clsidString);

    // (既定値) = <説明>
    error = WriteRegistryStringValue(HKEY_CLASSES_ROOT,
            keyPath, NULL, description);
    if (error != ERROR_SUCCESS) return SELFREG_E_CLASS;

    // CLSID\<クラス ID>\InProcServer32 のパスを作成
    lstrcat(keyPath, L"\\InProcServer32");

    // (既定値) = <DLL のパス>
    error = WriteRegistryStringValue(HKEY_CLASSES_ROOT,
            keyPath, NULL, modulePath);
    if (error != ERROR_SUCCESS) return SELFREG_E_CLASS;

    // ThreadingModel = <スレッディングモデル>
    error = WriteRegistryStringValue(HKEY_CLASSES_ROOT,
            keyPath, L"ThreadingModel", threadingModel);
    if (error != ERROR_SUCCESS) return SELFREG_E_CLASS;

    // Software\…\ShellIconOverlayIdentifiers\<説明> のパスを作成
    wsprintf(keyPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\%s", description);

    // (既定値) = <クラス ID>
    error = WriteRegistryStringValue(HKEY_LOCAL_MACHINE,
            keyPath, NULL, clsidString);
    if (error != ERROR_SUCCESS) return SELFREG_E_CLASS;

    return S_OK;
}

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

RegisterIconOverlayHandler は、
クラス 1 つの登録処理をまとめたものだ。
レジストリファイルでは数行でも、
API を使って登録するには結構手間が掛かる。

StringFromGUID2 は、構造体であるクラス ID を、
中括弧で囲んだおなじみの表現に書式化する関数である。
レジストリではほとんどが文字列なので、
この関数や、逆の処理を行う CLSIDFromString 関数の
お世話になることが多い。

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

// COM クラスと重ね合わせアイコンハンドラの登録解除
static HRESULT UnregisterIconOverlayHandler(
        REFCLSID classID,
        LPCTSTR description) {

    // 軽いチェック
    if (description == NULL) return E_UNEXPECTED;

    // CLSID を文字列に
    WCHAR clsidString[40];
    if (!StringFromGUID2(classID, clsidString, 40)) return SELFREG_E_CLASS;

    WCHAR keyPath[256];

    LONG error = ERROR_SUCCESS;

    // Software\…\ShellIconOverlayIdentifiers\<説明> のパスを作成
    wsprintf(keyPath, L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ShellIconOverlayIdentifiers\\%s", description);

    // (既定値) を削除
    error |= DeleteRegistryValue(HKEY_CLASSES_ROOT, keyPath, NULL);
    error |= RegDeleteKey(HKEY_CLASSES_ROOT, keyPath);

    // CLSID\<クラス ID>\InProcServer32 のパスを作成
    wsprintf(keyPath, L"CLSID\\%s\\InProcServer32", clsidString);

    // ThreadingModel と (既定値) を削除
    error |= DeleteRegistryValue(HKEY_CLASSES_ROOT, keyPath, L"ThreadingModel");
    error |= DeleteRegistryValue(HKEY_CLASSES_ROOT, keyPath, NULL);
    error |= RegDeleteKey(HKEY_CLASSES_ROOT, keyPath);

    // CLSID\<クラス ID> のパスを作成
    wsprintf(keyPath, L"CLSID\\%s", clsidString);

    // (既定値) を削除
    error |= DeleteRegistryValue(HKEY_CLASSES_ROOT, keyPath, NULL);
    error |= RegDeleteKey(HKEY_CLASSES_ROOT, keyPath);

    return error ? S_FALSE : S_OK;

}

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

UnregisterIconOverlayHandler は登録の解除を行う。
RegisterIconOverlayHandler の逆順に処理をしている。
登録解除の場合は、値を削除するだけでなく、
RegDeleteKey を使ってキーも削除する必要がある。

さて、これら関数は DllRegisterServer や
DllUnregisterServer から呼ばれる予定なのだが、
処理の原子性が保障されていない。
これは、関数の途中でエラーが起きた場合は、
中途半端に登録された状態となりうる事を意味する。

幸い、DllRegisterServer や DllUnregisterServer は、
処理に失敗した際には中途半端な状態になっても構わないと、
ドキュメントに明記されているため、
安心して上記の関数を使うことができるのである。

明日は、いよいよ仕上げとなる。