
TechNet コラムへようこそ。このコラムでは、よく寄せられるシステム管理スクリプトに関する質問に Scripting Guys がお答えします。システム管理スクリプトについて質問がある場合は、scripter@microsoft.com (英語のみ) までお送りください。すべての質問に回答することはできないかもしれませんが、可能な限り対応いたします。
詳細情報
| • | |
| • | |
| • |
![]()
Scripting Guy さん、よろしくお願いします。レジストリ キーのすべての値を列挙できるスクリプトはありますか。
-- DG

DG さん、こんにちは。先週末、このコラムを執筆している Scripting Guy はドーナツを買いに出かけましたが、本来よりもずっと時間がかかりました。なぜドーナツを買うだけでそれほど時間がかかったのかというと、それは、彼の前に並んでいた女性が、陳列ケースに並んでいるドーナツ 1 つ 1 つの栄養価について店員に根掘り葉掘り質問していたからです。途中で彼女は考え込んで、こう言いました。「じゃあ、どれが一番健康に良いと思いますか」と。
ちょっと、何を言っているんですか、あなたはドーナツ屋にいるんですよ。一番健康に良いのは、回れ右をして逃げ出すことです。一番健康に良い、ですって。とんでもない。この店に入るだけで寿命が 1 〜 2 年縮まりますよ。
やれやれ。
とにかく、ドーナツ屋の店員 (このコラムを執筆している Scripting Guy よりはるかに忍耐強くて思いやりがありました) は、ブルーベリー ケーキ ドーナツを勧めました。念のため、このコラムを執筆している Scripting Guy は、伝統的なグレーズド ドーナツとブルーベリー ケーキ ドーナツの栄養価を調べて比較してみました (彼はこの作業をすべて勤務中に行ったのか、ですって。ええと、彼は…。この情報を見つけた場所や方法を覚えていません)。
彼が調べたのは次のような情報です。
- | ブルーベリーケーキドーナツ | グレーズドドーナツ |
エネルギー | 290 | 210 |
総脂質 | 16 g | 10 g |
脂質からのエネルギー | 140 | 90 |
つまり、実は、ブルーベリー ケーキ ドーナツはグレーズド ドーナツよりも健康に悪いのです。これは、店員のうっかりミスでしょうか。それとも、この女性にブルーベリー ケーキ ドーナツを食べさせて、彼女がその場で急死し、再び来店して一番健康に良いドーナツについてのばかげた質問することがないようにと思って勧めていたのでしょうか。わかりませんね。このコラムを執筆している Scripting Guy の望みは、12 個のドーナツ (すべてケーキ ドーナツです) を買って帰ることだけでしたから。
実際、健康に良い物を探しているのなら、ドーナツ屋は、そのような探し物をするのに最適な場所ではありません。探しものに最適な場所は、スクリプト センターです。ここには、レジストリ キーに含まれているすべての値 (およびその値に含まれている値) を列挙できるスクリプトなど、ありとあらゆる健康に良いものを取り揃えています。健康的な次のスクリプトをお楽しみください。
Const HKEY_CURRENT_USER = &H80000001
Const REG_SZ = 1
Const REG_EXPAND_SZ = 2
Const REG_BINARY = 3
Const REG_DWORD = 4
Const REG_MULTI_SZ = 7
strComputer = "."
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
strKeyPath = "Software\Microsoft\Internet Explorer\Main"
objRegistry.EnumValues HKEY_CURRENT_USER, strKeyPath, arrValueNames, arrValueTypes
For i = 0 to UBound(arrValueNames)
strText = arrValueNames(i)
strValueName = arrValueNames(i)
Select Case arrValueTypes(i)
Case REG_SZ
objRegistry.GetStringValue HKEY_CURRENT_USER,strKeyPath, strValueName,strValue
strText = strText & ": " & strValue
Case REG_DWORD
objRegistry.GetDWORDValue HKEY_CURRENT_USER,strKeyPath, strValueName, intValue
strText = strText & ": " & intValue
Case REG_MULTI_SZ
objRegistry.GetMultiStringValue HKEY_CURRENT_USER,strKeyPath, strValueName,arrValues
strText = strText & ": "
For Each strValue in arrValues
strText = strText & " " & strValue
Next
Case REG_EXPAND_SZ
objRegistry.GetExpandedStringValue HKEY_CURRENT_USER,strKeyPath, strValueName,strValue
strText = strText & ": " & strValue
Case REG_BINARY
objRegistry.GetBinaryValue HKEY_CURRENT_USER,strKeyPath, strValueName,arrValues
strText = strText & ": "
For Each strValue in arrValues
strText = strText & " " & strValue
Next
End Select
Wscript.Echo strText
Next
このスクリプトは便利かもしれないけれど、どうやってスクリプトで健康になるのか、ですって。ちょっと、私たちをだれだと思っているんです。医者だとでも思っているのですか。私たちにはスクリプトで健康になるしくみなんて説明できません。とにかく、健康になるのです。
少なくとも、スクリプトで健康を損ねることはありません。
まあ、私たちの知る限りはそうです。
さて、この医学的なたわ言はちょっと置いておいて、このスクリプトのしくみを説明しましょう。ご覧のとおり、まず、いくつかの定数を定義します。ここでは、HKEY_CURRENT_USER 定数を使用して、処理対象のレジストリ ハイブをスクリプトに通知します。他の定数は、処理する各レジストリ値のデータ型を特定するのに使用します。たとえば、WMI ではレジストリ値のデータ型が 1 の場合、その値は REG_SZ データ型になります。したがって、REG_SZ 定数に値 1 を代入します。
Const REG_SZ = 1
妥当で健康に良いと思いませんか。
すべての定数を定義したら、次はローカル コンピューターの WMI サービスに接続します。もちろん、これとまったく同じスクリプトをリモート コンピューターに対しても実行できます。必要なのは、そのリモート コンピューターの名前を strComputer 変数に代入することだけです。
strComputer = "."
ついでに、WMI の接続文字列をよくご覧ください。
Set objRegistry = GetObject("winmgmts:\\" & strComputer & "\root\default:StdRegProv")
この接続文字列については、注意事項が 2 つあります。1 つ目は、root\default 名前空間に接続することです。これは少し珍しいことです。WMI スクリプトを作成する場合、通常は root\cimv2 名前空間に接続します。2 つ目は、StdRegProv クラスに直接バインドすることです。これは、すべてのレジストリ メソッドが静的メソッドだからです。つまり、これらのメソッドは、クラスの各インスタンスではなく、クラス全体を操作します。
注: あまり心配しないでください。ちょっと自慢しようとしていただけです。そういえば、1998 年に Winchell's Donut House が重さ 2 t 以上、直径 30 m 近くもあるドーナツを作ったことはご存じでしょうか。ただし、残念なことに、このドーナツが通常メニューの仲間入りを果たすことはありませんでした。 |
WMI サービスに接続したら、次のコード行を使用して、(HKEY_CURRENT_USER ハイブ内の) 処理対象のレジストリ パスを strKeyPath という名前の変数に代入します。
strKeyPath = "Software\Microsoft\Internet Explorer\Main"
レジストリ パスを代入したら、EnumValues メソッドを使用して、このレジストリ キーに含まれているすべてのレジストリ値のコレクションを取得します。
objRegistry.EnumValues HKEY_CURRENT_USER, strKeyPath, arrValueNames, arrValueTypes
ここから、少し風変わりになってきます。まず、EnumValues メソッドは、指定したレジストリ キーに含まれているすべての値の名前とデータ型を返します (そのレジストリ キーのサブキーは対象外です。サブキーに含まれている値の名前とデータ型を返すには、再帰関数を記述する必要があります)。たとえば、このサンプル スクリプトでは Main レジストリ キーに Anchor Underline という名前の値が含まれていて、Anchor Underline のデータ型が REG_SZ であることがわかります。これは便利ですが、Anchor Underline は yes に設定されているのでしょうか。それとも、Anchor Underline は no に設定されているのでしょうか。この点については、EnumValues メソッドではわかりません。自力で特定する必要があります。
がっかりすることはありません。値の名前とデータ型がわかっていれば、実際の値を特定するのはそれほど難しくありません (少し厄介かもしれませんが、それほど難しくありません)。EnumValues メソッドを呼び出すときに、次の 4 つのパラメーターを指定したことにお気付きの方もいらっしゃるでしょう。
| • | HKEY_CURRENT_USER: 処理対象のレジストリ ハイブをスクリプトに指示する定数です。 |
| • | strKeyPath: 処理対象のレジストリ キーをスクリプトに指示する変数です。 |
| • | arrValueNames: EnumValues メソッドによって、レジストリ キーに含まれるすべての値の名前が代入される出力パラメーターです。出力パラメーターは単に、メソッドによって返されるデータを保持するパラメーターです。EnumValues メソッドに変数名を指定すると、EnumValues メソッドによって各レジストリ値の名前が返され、返された情報が出力パラメーターに代入されます。 |
| • | arrValueTypes: 別の出力パラメーターです。このパラメーターは、レジストリ キーに含まれるすべての値のデータ型を保持します。 |
EnumValues メソッドから返されたすべての情報をどうするのか、ですって。まず、値の名前が格納された配列 (arrValueNames) を処理する For Next ループを設定します。
For i = 0 to UBound(arrValueNames)
このループ内では、最初のレジストリ値の名前を取得して、2 つの変数に代入します。
strText = arrValueNames(i) strValueName = arrValueNames(i)
strText 変数は、各レジストリ値の名前と値の両方を保持するために使用します。一方、strValueName 変数は、レジストリ値の値を取得するときに、この値を示すために使用します。
注: "値" という言葉を 1 文でこれ以上使用できるのか、ですって。できないといいのですが…。 |
レジストリから情報を読み取る必要がある場合、Scripting Guys は必ず、Windows スクリプト ホスト (WSH) ではなく WMI を使用します。その理由は、WMI が使いやすいからではありません。実際、WMI は WSH よりもかなり使うのが難しいです。これは、WMI ではレジストリのデータ型ごとに異なるメソッドを使用する必要があるためです。値のデータ型が REG_SZ の場合は、GetStringValue メソッドを使用する必要があります。値のデータ型が REG_DWORD の場合は、GetDWORDValue メソッドを使用する必要があります。これに対して、WSH では RegRead という 1 つのメソッドですべてのデータ型を読み取ることができます。
では、なぜ WSH ではなく WMI を必ず使用するのかというと、理由は 1 つだけで、WMI を使用するとリモート コンピューターのレジストリを読み取ることができるからです。この処理は WHI ではできません。
とにかく、WMI ではデータ型ごとに異なるメソッドが必要だという点から、次のコード行の役割はおわかりいただけるでしょう。
Select Case arrValueTypes(i)
ここでは、最初のレジストリ値のデータ型を調べています。このレジストリ値のデータ型が REG_SZ だとしましょう (かなりさかのぼることになりますが、スクリプトの冒頭で REG_SZ という名前の変数を定義しましたね)。この場合は、GetStringValue メソッドを使用してレジストリから値を読み取り、その値を strText 変数に格納します。
Case REG_SZ
objRegistry.GetStringValue HKEY_CURRENT_USER,strKeyPath, strValueName,strValue
strText = strText & ": " & strValue
言うまでもなく、値のデータ型が異なる場合は別のメソッドを使用します。GetMultiStringValue メソッドと GetBinaryValue メソッドでは、データが配列の形式で返されることに注意した方が良いでしょう。つまり、この配列のすべての値を取得する For Each ループを設定する必要があります。
Case REG_MULTI_SZ
objRegistry.GetMultiStringValue HKEY_CURRENT_USER,strKeyPath, strValueName,arrValues
strText = strText & ": "
For Each strValue in arrValues
strText = strText & " " & strValue
Next
strText 変数の値をエコー バックしたら、ループの先頭に戻り、arrValueNames 配列の次の項目に対して同じ処理を繰り返します。最終的には、次のような結果が出力されます。
NoUpdateCheck: 1 NoJITSetup: 1 Disable Script Debugger: no Show_ChannelBand: No Anchor Underline: yes Cache_Update_Frequency: Once_Per_Session Display Inline Images: yes
このような具合です。
DG さん、これでうまくいくでしょう。質問がある場合はお知らせください。ちなみに、毎日コップに 8 杯の水を飲まなくてはならないという古い格言が、研究者によって、もはや水も漏らさぬ正しい理屈ではないとされていることにお気付きでしょうか。『Journal of the American Society of Nephrology』(英語) の論説によれば (もちろん私たちはこの雑誌を講読しています。皆さんもそうですよね)、この誤解の原因の 1 つとして、次のように述べている米国食品栄養委員会の 1945 年の報告書の誤読が挙げられるそうです。
成人における適切な水分の摂取量は、ほとんどの場合、1 日 2.5 リットルである。さまざまな人に対する通常の基準は、食品 1 カロリーあたり 1 ミリリットルである。調理済み食品には、この量のほとんどが含まれている。 |
この段落の 3 つの文をすべて読めば、食事をするだけで 1 日に必要な量の水分を摂取できることがわかります。しかし、そこはアメリカ人ですから、わざわざ 3 つの文をすべて読もうと思った人がいたでしょうか。アメリカ人には、最初の文だけを読んで残りは無視する傾向があるのです。このようにして、毎日コップに 8 杯の水を飲まなくてはならないという俗説が生まれました。
注: 確かに、本当はこの話にもう少し補足することがあるのですが、詳細すべてをわざわざ書くつもりはありません。ちょっと、私たちをだれだと思っているんです。プロの物書きだとでも思っているのですか。 |
では、なぜ "1 日にコップ 8 杯の水" の話を持ち出したのかというと、医療関係者が、1 日にコップ 8 杯の水が必要だと言ったのは間違いでした。ということは、1 日にコップ 8 杯の水ではなく、8 個のドーナツが必要になるはずではないでしょうか。Scripting Guys は指を重ねて願っているとだけ言っておきましょう。
ええ、というより、丸々とした指を重ねて願っています。