2006 年 7 月 11 日 17 時 11 分

レジストリ操作


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


シェルのカラムハンドラを登録するためには、
レジストリを操作しなければならない。

.NET にもレジストリ操作のためのクラスはある。
珍しいことに、System 名前空間の中に存在せず、
Microsoft.Win32 名前空間にある、
Registry や RegistryKey クラスである。

カラムハンドラの登録は、
HKEY_CLASSES_ROOT\Folder\shellex\ColumnHandlers の下に、
クラスの GUID の文字列表記のキーを作成することで行う。

では、クラスを使って登録してみよう。

    [ComRegisterFunction]
    private static void Register(Type t) {

        // 自分と違うクラスなら何もしない
        if (t != typeof(ColumnProvider)) return;

        // レジストリ形式の GUID 文字列を取得
        // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
        string clsid = t.GUID.ToString("B");

        // (HKCR\)Folder\shellex\ColumnHandlers\<GUID>
        string path = "Folder\\shellex\\ColumnHandlers\\" + clsid;

        // レジストリキーを開くまたは作成
        RegistryKey key = Registry.ClassesRoot.CreateSubKey(path);

        // null が返ればアクセス権の関係で失敗とする
        if (key == null) throw new UnauthorizedAccessException();

        // 例外が起きても最後に Dispose を呼ぶ
        using (key) {

            // キーの値は必須ではない。適当に入れておく。
            key.SetValue(null, "Column Provider Test");

        }

    }

まず、念のため自分のクラスに対する登録かどうかを調べる。
これは、クラスの型を比較するだけでよい。
クラスの型は System.Type 型のインスタンスであり、
C# では typeof(型)やインスタンス.GetType() で取得する。

.NET のクラスはそれぞれ固有の Type インスタンスを持ち、
どのように Type を取得したかに関係なく、
示すクラスが同じなら Type は必ず同じインスタンスである。

引数の t を typeof(ColumnProvider) と比較することで、
ColumnProvider の登録作業かどうかを判定するのだ。

続いて、クラスをレジストリに登録するため、
クラスID(CLSID)を文字列に書式化する

CLSID は、GuidAttribute で明示しているが、
これは、型インスタンスの GUID プロパティで取得できる。
また、GUID 構造体は、ToString に書式 "B" を指定すれば、
レジストリ表記(中括弧)で書式化してくれる。

続いて、レジストリを開く。

Registry クラスのフィールドに定義されている、
レジストリのルートを表す RegistryKey インスタンスを取得し、
CreateSubKey メソッドを呼ぶことでキーを作る。
CreateSubKey は既にキーがあっても例外を発生させず、
既存のキーを開いて返すという特徴がある。

RegistryKey クラスは、IDisposable を実装しているので、
using ステートメントを使って囲ってやることで、
使用後に自動的に Dispose を呼び出すようにしておく。

ただ、CreateSubKey メソッドにはこんなことが書いてある。
「操作が失敗した場合は null を返します。」
統合的に例外で処理できないのは面倒なので、
null が返って来たら UnauthorizedAccessException とする。

ちなみに、using ステートメントには、
null 参照の変数を渡しても構わないので、
CreateSubKey の null チェック等は、
using の内部に入れてしまって構わない。

using に変数定義や CreateSubKey 呼び出しまで書くと、
極端に行が長くなってしまうので避けただけの話だ。

また、カラムハンドラの登録では、
キーさえ作成すれば、値の登録は必要ないが、
CLSID の役割がわかるように、説明を登録しておこう。

値の登録は、SetValue メソッドで行う。
値は名前と内容(データ)の組み合わせだが、
第 1 引数が null の場合、名前のない値(既定値)を指す。
「Column Provider Test」とでも書いておこう。

登録解除も考え方は同じだ。

    [ComUnregisterFunction]
    private  static void Unregister(Type t) {

        // 自分と違うクラスなら何もしない
        if (t != typeof(ColumnProvider)) return;

        // レジストリ形式の GUID 文字列を取得
        // {xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
        string clsid = t.GUID.ToString("B");

        // (HKCR\)Folder\shellex\ColumnHandlers\<GUID>
        string path = "Folder\\shellex\\ColumnHandlers\\" + clsid;

        // レジストリを開いてみる
        RegistryKey key = Registry.ClassesRoot.OpenSubKey(path);

        // 開けなかったら存在しないので OK
        if (key == null) return;

        // 開けた場合は必ず閉じる
        key.Close();

        // キーを削除する
        Registry.ClassesRoot.DeleteSubKeyTree(path);

    }

RegistryKey には、キーの存在を調べるメソッドがない。
そのため、OpenSubKey メソッドを使って確認する。
OpenSubKey はキーが存在しない場合は null を返すのだ。

DeleteSubKeyTree メソッドは、指定したキー配下を
丸ごと容赦なく消し去るメソッドだ。
手抜きとも言えるのだが、レジストリの位置を考えると、
キーの配下を他のプログラムが利用している可能性は低い。
なので、これで問題ないと思う。

さて、ビルドしてみよう。

「Unregister: LoaferShellEx.Column.ColumnProvider」

と表示された。これは昨日の DLL の登録解除だな。

正常に登録されたことを確認するために、
レジストリエディタを開く。
よし、ColumnHandlers 配下に CLSID があった。

今度は登録解除をしてみよう。
例によって、故意に文法エラーを起こさせてビルドする。

レジストリエディタの表示を更新し、
ColumnHandlers 配下を見る。
よし、CLSID がなくなっていることを確認できた。

これら二つのメソッドは、
COM オブジェクトのインストール(再インストール含む)と
アンインストールに相当する。

レジストリ以外でも、ライブラリを実行する上で、
必要な初期化があれば、ここで実装すればいいということだ。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.