susume cyuukyuu programer

第2号ついに完成!

級プログラマの中級プログラマによる中級プログラマのためのWebマガジン「進め!中級プログラマー」がついに第2号にいたりました。(ぱちぱち)

こでは、僕が Windowsプログラミングの学習をする過程で作成したドキュメントを基に、毎号一つのテーマを決めて Windowsプログラミングに関する概念やテクニックを掲載していく予定としています。
プロ・アマを問わずウィンドウズプログラミングに興味のある皆様のお役に立てれば幸いです。

のところ、主にInprise社の「Delphi」及び「C++ Builder」に関する話題を中心に構成していく予定としていますが、余裕ができてくれば「Java」やプログラミングに関する一般的な話題についても加えていきたいと思っています。

読者の皆様へお願い

でも触れましたとおり、この「進め!中級プログラマー」は僕がかなりアバウトに作成したドキュメントが基となっていますので、内容にタイプミス、不適切な表現、勘違いが相当数あるものと思います。
ご面倒だとは思いますが、そのような誤り等を発見されました方は是非杉浦までご連絡いただけますようよろしくお願いいたします。(指摘されました点に関しましては確認でき次第、内容に反映させていきたいと思っております)

た、いつものお約束事ですが「この文書に書かれた内容によって生じたいかなる損害に対しても当方は責任を持ちません。自己責任のもとで当文書をご利用下さい」ということでお願いします。
お、「進め!中級プログラマー」に掲載されていますプログラムコードや各種解説文に関しましては「その使用に関しては自由、転載も情報の出所(当Webサイト)を明記した場合自由」とさせていただきますのでよろしくお願いいたします。

バックナンバー

創刊号 --- シェルと仲良くする part1


--- 1998 10/25 発刊 ---
--- 1999 05/16 改修 ---

Thanks to 沖本 憲昭 さん
第 2 号

今回のテーマ

シェルと仲良くする(part2)

    2. 拡張シェルオブジェクト

      2.1 拡張シェル登録に係るレジストリ操作

      2.2 拡張シェルオブジェクト用インターフェイス

      2.3 拡張シェルオブジェクト例題

次号予告

進め!中級プログラマー 第2号
今回のテーマ シェルと仲良くする(part2)

VCLのような良く出来たライブラリを用いると、そこそこのWindowsアプリケーション、ユーティリティがものの数時間で非常に簡単に作成できてしまいます。
かしながら、それら標準のライブラリを用いても簡単には実現しない機能もやはり多数存在します。
回の特集の主題でもあるシェルとの対話もそれらの1つです。

各ファイルに割当てられているアイコンを取得するにはどうすればいいのか。
ある拡張子のファイルに対して、エクスプローラ上の右クリックで非標準のコンテキストメニューを表示させるにはどうすればいいのか。

刊号に引き続き、このようなシェルとの対話を必要とする場面で使える知識やテクニックを以下に解説していきます。

2. 拡張シェルオブジェクト

号で解説したとおり、Windowsにおいてシェルと対話する最も簡単な方法はシェルAPIを利用するものです。
かしながら、シェルAPIだけではいかんともしがたいこともよくあります。シェルの標準で用意されている機能に絡んだことを行う場合であればシェルAPIだけでそこそこ実現できますが、標準ではない動作をシェルに行わせたい場合、例えば、前述の「ある拡張子のファイルに対して、エクスプローラ上の右クリックで非標準のコンテキストメニューを表示させたい」場合などはシェルAPIだけでは実現不可能です。
のようなシェルの非標準の動作を実現させるためには、「拡張シェルオブジェクト」と呼ばれる COMオブジェクト(厳密にはインプロセスActiveXサーバー)を実装し、それを Windowsに登録する必要があります。この拡張シェルオブジェクトは、Delphiでは「shlobj.pas」(C系言語であれば「shlobj.h」)に宣言されているインターフェイス群(表2-1参照)を実装することで作成します。
だし単純な非標準動作であれば、これらの拡張シェルオブジェクトを作成するまでもなくレジストリを少しいじるくらいで簡単に実現できてしまう場合もあります。(これに関しては後程例を示して解説します)
こではまず、拡張シェルを Windowsに登録する上で必要なレジストリについて解説し、その後、拡張シェルオブジェクトの骨組みとなるインターフェイス群について解説し、最後にそれら拡張シェルオブジェクトが必要になる場合の具体例をいくつか示していくことにします。

2.1 拡張シェル登録に係るレジストリ操作

張シェルを機能させるためには、必ずそれを Windowsに登録(すなわちレジストリに書き込み)しなくてはなりません。
これは、拡張シェルオブジェクトを使用したかどうかに関らず必要な作業です。(後で触れるように、簡単な拡張であれば拡張シェルオブジェクトを実装する必要は無いことに注意して下さい)
ぜなら、シェルはその動作において、該当するレジストリをチェックすることで動作を拡張する必要があるかどうかを判断するからです。

2.1.1 下準備

ずは拡張シェルを登録する上での下準備を「HKEY_CLASSES_ROOT」に対して行います。

本的には、拡張シェルを必要とするファイルの「拡張子」とそのファイルを扱うアプリケーションの「AppID」(アプリケーションID)をレジストリ「HKEY_CLASSES_ROOT」の下に書き込めば完了です。ここで「基本的には」と書いたのは、特定のファイルタイプに対してシェルを拡張するのではなく全てのファイルに対してシェルを拡張する場合にはこの作業が不要だからです。(この場合の拡張シェル登録方法に関しましては後述します)

下に、ファイル拡張子が「.sct」であるファイルに対して拡張シェルを登録する場合に実行される下準備用レジストリファイル(SCT.REG)を示します。ただし、ここでは拡張子「.sct」のファイルは AppIDが「EasySecret.Document.1」であるアプリケーションによって取扱われるものと仮定しています。
論、上記レジストリファイルを実行しなくても、同じ内容をレジストリエディタを用いて手打ちで登録すれば問題ありません。
SCT.REG

REGEDIT4

[HKEY_CLASSES_ROOT\.sct]
@= "EasySecret.Document.1"

[HKEY_CLASSES_ROOT\EasySecret.Document.1]
@= "A EZ-Secreted File"

初の2行により、拡張子「.sct」のキーが作成され、その拡張子のファイルを扱うアプリケーションのAppID「EasySecret.Document.1」が登録されます。
の2行によりその AppID「EasySecret.Document.1」のキーが作成され、その説明文「A EZ-Secreted File」が登録されます。この"A EZ-Secreted File"はAppIDに関する単なる説明文であって特に意味はありません。

れによりシェルがどう動作するのかを簡単な例で見てみます。
クスプローラで拡張子が「.sct」となっているファイルがダブルクリックされた場合、まずシェルはレジストリからキー名が「.sct」となっているキーを探し出し、そのキーのデフォルトの値「EasySecret.Document.1」をAppIDとして取出します。
これにより、シェルは該当ファイルを取扱うアプリケーションのAppIDを知ったことになります。
に、その取出したAppID「EasySecret.Document.1」がキー名となっているキーへと移動し、後はそこで指定されているアプリケーションを(ダブルクリックされたファイル名を渡して)起動させればシェルの仕事は完了というわけです。(上記のSCT.REGではアプリケーションまでは指定していないので注意して下さい。SCT.REGはあくまでも最小限の例です。)

のように、拡張子のキーとその拡張子のファイルを扱うアプリケーションのAppIDのキーは必ず作成、登録しておく必要があります。とにかくこれで下準備は完了です。

ミニ解説: IID, CLSID, AppID, ProgID

Windowsプログラミングに絡んで使用されるIDのうち、重要なものは「IID」「CLSID」「AppID」「ProgID」の 4つであろうと思います。
最初はこれらの違いをわかりにくく感じられるのではないでしょうか。
下に、これらの違いをおおざっぱに説明します。詳しいことは各種書籍を参照して下さい。

IID.......COMクラス中のインターフェイスの識別子(GUID)
CLSID.....COMクラスの識別子(GUID)
AppID.....アプリケーションを表す文字列で、ProgIDと同じ用途で使用される( ProgIDと同じ物を与えても構わない)
ProgID....CLSIDを参照する文字列("{プログラム名}.{コンポーネント名}.{バージョン番号}"の形式)

ProgIDと AppIDの違いは、形式文字列か単なる文字列かの違いのみで、大抵の場合は同じものと考えて問題はありません。(細かいことを言うと、 AppIDは ProgIDを"集合"という意味で「含んでいる」関係にあります)

ミニ解説: レジストリファイル

ジストリへのキーや値の追加はレジストリファイルを実行して行うのが楽だと僕は思います。
下にレジストリファイルの書式を簡単に説明します。詳しくは関連書籍を参照して下さい。

REGEDIT4

; Comment
[KeyLocation]
@= "Default Value"
"DataName" = "Data Value"

行目の「REGEDIT4」は必須です。おまじないみたいなもんだと思って下さい。(実は「REGEDIT」というのでも良いんですが、これだと書式が上のものとは別になってしまいます。)
行目の「Comment」はいわゆるコメント文です。必ず先頭に「;」をつけて下さい。
行目の「KeyLocation」はキーの場所(HKEY_CLASSES_ROOT \.bmp 等)を示します。上のように"[ ]"で囲んで下さい。
行目の「Default Value」は3行目の「KeyLocation」のデフォルトの値です。"@="ではじめて下さい。
行目の「DataName」及び「Data Value」はそれぞれ、3行目の「KeyLocation」に属するデータ名とその値を示しています。
4行目、5行目の注意点としては、クオーテーション""で囲まれた部分にファイルパスを入れる時など、セパレータ「\」は「\\」と2つ重ねる必要がある点です。

れら2行目以降の組合せでレジストリファイルは成り立っています。
た、忘れがちですが最後の行には必ず改行を入れて下さい。これを忘れると最後の行の部分が登録されません。

れだけではまだまだ良く分からないという方は、一度エクスプローラで拡張子が「.reg」のファイルを検索してメモ帳などで見てみることをお勧めします。おそらくそれで大体の感覚がつかめると思います。

2.1.2 デフォルトアイコンの設定

る拡張子のファイル全てに付与されるデフォルトアイコン(静的)を設定したり変更したりする場合は拡張シェルオブジェクトを実装するまでもなく、レジストリに手を加えるだけで実現できます。
こでは、このデフォルトアイコンの設定方法を解説します。

ず、対象となる拡張子をキー名とするキーにサブキー「DefaultIcon」を作成します。この「DefaultIcon」キーのデフォルトの値にアイコンを含む実行ファイルへのフルパスとアイコンインデックスを指定すれば完了です。
下の例は、前節で導入した拡張子が「.sct」となるファイルのデフォルトアイコンを「D:\EZSECRET\EZSECRET.DLL」中の最初のアイコンに設定するため実行されるレジストリファイル(SCTICO.REG)です。

お、同じ拡張子でもファイル毎に異なるアイコンを付与したい場合は、アイコンハンドラと呼ばれる拡張シェルオブジェクトを作成する必要があります。これに関しては後の章で解説する予定です。
SCTICO.REG

REGEDIT4

[HKEY_CLASSES_ROOT\.sct\DefaultIcon]
@= "D:\\EZSECRET\\EZSECRET.DLL,1"

イコンを指定する場合はこの例のように、アイコンを含む実行ファイル(EXE,DLL)へのフルパス及び該当アイコンのインデックスをコンマ区切りで記述してやる点に注意して下さい。

2.1.3 デフォルトコンテキストメニューの追加

る拡張子のファイルを右クリックすると現れるコンテキストメニューに、静的なメニュー項目をさらに追加したい場合は、拡張シェルオブジェクトを実装するまでもなく、レジストリに手を加えるだけで実現できます。
こでは、このデフォルトコンテキストメニューの追加方法を解説します。

ず、対象となる拡張子のキーに登録している AppIDをキー名とするキー(先の「.sct」ファイルでの「EasySecret.Document.1」に相当)にサブキー「shell」を作成します。
さらにこの「shell」キーに適当な名前のサブキー(仮に subとします)を作成し、そのデフォルトの値に、(追加するメニューに)表示させたい文字列を指定します。
そして最後に、先ほど作成した「sub」キーにサブキー「command」を作成し、そのデフォルトの値に該当メニューがクリックされた際に実行させるコマンドライン文を指定すれば作業は完了です。
下の例は、前述の拡張子が「.sct」となるファイルに対して「デフォルト復号化」と表示されるコンテキストメニューを追加するため実行されるレジストリファイル(SCTCNTXT.REG)です。ただし、追加したメニューがクリックされるとコマンドライン「D:\EZSECRET\EZSECRET.EXE /D "%1"」が実行されるものとしています。

お、同じ拡張子でもファイル毎に異なるコンテキストメニューを付与したい場合は、コンテキストメニューハンドラと呼ばれる拡張シェルオブジェクトを作成する必要があります。これに関しては後の章で解説する予定です。
SCTCNTXT.REG

REGEDIT4

[HKEY_CLASSES_ROOT \EasySecret.Document.1 \shell \DfltDecode]
@= "デフォルト復号化"

[HKEY_CLASSES_ROOT \EasySecret.Document.1 \shell \DfltDecode \command]
@= "D:\\EZSECRET\\EZSECRET.EXE /D %1"

マンドライン中の %1 は選択されているファイル名を指します。ちなみに %2 はプリンター名を指します。

お、メニューに表示される文字列が与えられているキー(上例では「DfltDecode」)には正規動詞と呼ばれる予約キーがあります。(表2-2参照
正規動詞はそれぞれ Windowsが標準で表示するコンテキストメニューに対応しています。
これらの正規動詞のいずれかがレジストリに指定されれば、対応する標準のコンテキストメニューがレジストリ中で指定されたものに変更されます。

表2-2 主な正規動詞
正規動詞 対応する標準コンテキストメニュー
open 開く
print 印刷
explore エクスプローラ
find 検索
properties プロパティ
2.1.4 新規作成ファイルタイプの追加

ァイルシステムのフォルダ上で右クリックすると表示される「新規作成」メニューで、とある拡張子のファイルを作成出来るようにする場合は拡張シェルオブジェクトを実装するまでもなく、レジストリに手を加えるだけで実現できます。
こでは、この新規作成ファイルタイプの追加方法を解説します。

ず、対象となる拡張子のキーにサブキー「ShellNew」を作成します。この「ShellNew」キーに、作成するファイルの形式を指定するデータ名を1つ作成し、その値を設定すれば作業は完了です。
「ShellNew」キーに作成できるデータ名、値、作成されるファイルのタイプの組合せを表2-3に示します。

表2-3 ShellNewキーに作成できるデータ名とその値
データ名 指定する値 作成されるファイルのタイプ
NullFile "" 空のファイルを作成します。
Data {バイナリー値} 指定されたバイナリー値を含むファイルを作成します。
FileName {ファイルのパス} 指定されたファイルのコピーが作成されます。
Command {実行させるコマンドライン} 指定されたコマンドライン(例えばウィザード)を実行します。

下の例は、前述した拡張子が「.sct」となるファイルを「新規作成」コンテキストメニューから作成出来るようにするため実行するレジストリファイル(SCTNEW.REG)です。
ここでは、「D:\EZSECRET\DEFAULT.SCT」のコピーが作成されるようにしています。
SCTNEW.REG

REGEDIT4

[HKEY_CLASSES_ROOT\.sct\ShellNew]
"FileName" = "D:\\EZSECRET\\DEFAULT.SCT"

だしこの場合、「新規作成」コンテキストメニューで表示されるメニュー名は、「2.1.1 下準備」での例を前提とすると、[HKEY_CLASSES_ROOT \EasySecret.Document.1]のデフォルトの値である「A EZ-Secreted File」となります。このように、新規作成コンテキストメニューでは AppIDをキー名とするキーのデフォルト値が表示されることに注意して下さい。
お、ファイルへのパスを例のようなフルパスにしない場合は、「???\Windows\ShellNew」ディレクトリに該当ファイルを入れておく必要がありますので注意して下さい。

2.1.5 拡張シェルオブジェクトの設定

で解説したような単純なシェルの拡張だけであればレジストリの設定のみで実現可能ですが、例えば「拡張子が同じでもファイルインスタンス毎に個別のアイコンやコンテキストメニューを表示させたい」というような複雑なシェルの拡張を行いたい場合は、拡張シェルオブジェクトを作成しそれを Windowsに登録しなくてはなりません。
こでは、拡張シェルを作成した後これらを Windowsに登録する方法を解説します。(拡張シェルオブジェクトの作成方法に関しましては後の節で解説します)

ず、「HKEY_CLASSES_ROOT\CLSID」キーに拡張シェルオブジェクトのクラスID(CLSID)をキー名とするキー(ここでは仮に {MY_ID} とします)を作成し、そのデフォルトの値に同オブジェクトに関する説明文を登録します。ここで、クラスID「MY_ID」は重複しないよう必ず UUIDGENなどのGUID生成ツールを用いて割当てて下さい。
それから「MY_ID」キーにサブキー「InProcServer32」を作成し、そのデフォルトの値に拡張シェルオブジェクト(DLL)のパスを登録します。さらに同サブキーにデータ名「ThreadingModel」とその値「Apartment」を登録します。
れで、拡張シェルオブジェクトをインプロセス ActiveXサーバーとして Windowsに登録できたことになります。しかし、まだ拡張シェルオブジェクトをどのシェル動作の拡張に充てるのかが登録できていません。
れを行うためにはまず、シェルを拡張させたいファイル拡張子のキーに登録している AppIDをキー名とするキー(先の「.sct」ファイルでの「EasySecret.Document.1」に相当)にサブキー「shellex」を作成します。(作業A)
それから「shellex」キーに拡張したい動作に対応するハンドラキー、例えば「ContextMenuHandlers」キー、を作成します。(ハンドラキーに関しましては表2-4にまとめています)
アイコンハンドラ以外のハンドラの場合、さらにそのハンドラキーに先ほど登録した拡張シェルオブジェクトのクラスID(CLSID)をキー名とするキー(先の例では {MY_ID})を作成すれば、ようやく Windowsにどのシェルの動作を拡張させるのか、またそのハンドラがどこにあるのか、を含めて全ての登録が完了したことになります。(クラスIDをキー名とするキーの代わりに、適当な名前のキーを作成しても構いませんが、その場合はそのキーのデフォルトの値にクラスIDを登録しなければなりません)
アイコンハンドラの場合はクラスIDをキー名とするキーを作成せず「IconHandler」キーのデフォルト値に直接、拡張シェルオブジェクトのクラスIDを登録して全ての登録が完了となります。
だし、特定のファイル拡張子を対象とするのではなく、全てのファイルオブジェクトやフォルダ、プリンタオブジェクトを対象にシェルを拡張させる場合は、上記作業Aの shellexサブキーを作成するキーを「 AppIDをキー名とするキー」から変更する必要があります。これに関しましては表2-5にまとめます。

表2-4 拡張する動作と対応するハンドラキー
拡張させる対象動作 ハンドラ名 ハンドラキー
コンテキストメニュー コンテキストメニューハンドラ ContextMenuHandlers
コンテキストメニューで「プロパティ」を選択した時に表示されるプロパティシート プロパティシートハンドラ PropertySheetHandlers
シェルで表示されるアイコン アイコンハンドラ IconHandler
フォルダオブジェクトの移動、コピー、削除 コピーフックハンドラ CopyHookHandlers
フォルダへのドラッグ・アンド・ドロップ ドラッグドロップハンドラ DragDropHandlers

表2-5 シェルの拡張対象とshellexサブキーを作成するキー
拡張させる対象オブジェクト shellexサブキーを作成するキー 設定可能なハンドラ
ある拡張子のファイル 該当拡張子のキーに登録されている AppID コンテキストメニューハンドラ
プロパティシートハンドラ
アイコンハンドラ
コピーフックハンドラ
全てのファイル HKEY_CLASSES_ROOT \* コンテキストメニューハンドラ
プロパティシートハンドラ
全てのフォルダ HKEY_CLASSES_ROOT \Folder コンテキストメニューハンドラ
プロパティシートハンドラ
コピーフックハンドラ
ドラッグ&ドロップハンドラ
全てのルート HKEY_CLASSES_ROOT \Drives 同上
全てのプリンタ HKEY_CLASSES_ROOT \Printers コンテキストメニューハンドラ
プロパティシートハンドラ
コピーフックハンドラ

下の例は、前述した拡張子が「.sct」となるファイルを対象に、クラスIDが { MY_ID } となる拡張シェルオブジェクト「D:\EZSECRET\EZSCNTXT.DLL」をコンテキストメニューハンドラとして登録するために実行するレジストリファイル(SCTREG1.REG)です。ただし、前述の「SCT.REG」の内容を前提としています。
SCTREG1.REG

REGEDIT4

[HKEY_CLASSES_ROOT \CLSID \{ MY_ID }]
@= "Easy Secret Context Handler"
[HKEY_CLASSES_ROOT \CLSID \{ MY_ID } \InProcServer32]
@= "D:\\EZSECRET\\EZSCNTXT.DLL"
"ThreadingModel" = "Apartment"

[HKEY_CLASSES_ROOT \EasySecret.Document.1 \shellex \ContextMenuHandlers \{ MY_ID }]
@= ""

下の例は、全てのファイルを対象に、クラスIDが { MY_ID } となる拡張シェルオブジェクト「D:\EZSECRET\EZSCNTXT.DLL」をコンテキストメニューハンドラとして登録するために実行するレジストリファイル(SCTREG2.REG)です。ただし、前述の「SCT.REG」の内容を前提としています。
SCTREG2.REG

REGEDIT4

[HKEY_CLASSES_ROOT\CLSID\{ MY_ID }]
@= "Easy Secret Context Handler"
[HKEY_CLASSES_ROOT\CLSID\{ MY_ID }\InProcServer32]
@= "D:\\EZSECRET\\EZSCNTXT.DLL"
"ThreadingModel" = "Apartment"

[HKEY_CLASSES_ROOT\*\shellex\ContextMenuHandlers\{ MY_ID }]
@= ""

次ページ( 2.2 拡張シェルオブジェクト用インターフェイス)に移動
次々ページ( 2.3 拡張シェルオブジェクト例題)に移動
トップページに戻る

メール作成 ご意見・ご要望等は左のポストをクリックして杉浦(gv4j-sgur@asahi-net.or.jp)までお寄せ下さい
なお、当サイトは「Netscape Communicator 4.5 for MS Windows」でのみ表示・動作が調整されております
Internet Explorer の場合、正常に表示・動作しない可能性がありますのでご了承下さい。
Copyright © 1998 Jun-ya Sugiura and Hanayo Sugiura. All rights reserved. Updated - 16 May 1999