
TechNet コラムへようこそ。このコラムでは、よく寄せられるシステム管理スクリプトに関する質問に Scripting Guys がお答えします。システム管理スクリプトについて質問がある場合は、scripter@microsoft.com (英語のみ) までお送りください。すべての質問に回答することはできないかもしれませんが、可能な限り対応いたします。
詳細情報
| • | |
| • | |
| • |
![]()
Scripting Guy さん、よろしくお願いします。ドライブを割り当てるアプリケーションとして HTA を使用しています。HTA を巧みに使用して、リスト ボックスに使用可能なドライブ文字が表示されるようにしたいと思っています。コンピュータで使用できるすべてのドライブ文字を動的に表示する方法はありますか。
-- JT

JT さん、こんにちは。つい先日、このコラムを執筆している Scripting Guy は、シアトル地域のスポーツ ファンにとって本当に散々な結果の週末になったことを嘆いていました。ですので、ようやく事態が好転してきたことをお伝えできてうれしく思っています。何しろ、昨夜、マリナーズのジェフ ウィーバー投手がオークランド アスレチックスに 8 対 7 で勝利し、今シーズン 7 度目の勝利を挙げたのですから。ジェフ ウィーバー投手はフリー エージェント選手で獲得した期待の選手で、"フリー" とは言うものの今年の年俸は 800 万ドル以上です。
7 勝に対して 800 万ドルの年俸というのは誤植ではありません。確かに、1 勝利あたりの報酬が 100 万ドル以上というのは多すぎるように思えます。でも、こう考えてみてください。ジェフ ウィーバー投手が今シーズン 7 試合で勝利を収めていなかったら、マリナーズはプレーオフ出場権を得ることができないでしょう。彼が 7 勝しても、800 万ドルの経費があることを考えると、マリナーズは間違いなく・・・。いや、何でもありません。計算方法を見直す必要があります。
注 : このコラムを執筆している Scripting Guy が、シアトル マリナーズのチームの運営方法に口を出す立場でないのは明らかです。しかし、そうは言っても、マリナーズはジェフ ウィーバー投手の獲得に 800 万ドルを費やしたにもかかわらず、プレーオフ出場権を得ることができませんでした。来年ジェフ ウィーバー投手との契約を解除し、彼の代理としてこのコラムを執筆している Scripting Guy にわずか 100 万ドルを支払うとします。その場合、マリナーズは、Scripting Guy を投手ローテーションに組み込むことでプレーオフ出場権を得られるでしょうか。おそらくないでしょうね。でも、700 万ドルの節約になります。 年俸 100 万ドルというのは、このコラムを執筆している Scripting Guy にとって大幅な減給になりますが、メジャー リーグのマウンドに立てるならそれもいといません。 |
しかし、信じがたいことかもしれませんが、ジェフ ウィーバー投手が今期 7 度目の勝利を挙げたというニュース以外にも、シアトル地域に衝撃を与えたエキサイティングなニュースがあります。ジェフ ウィーバー投手の偉業だけでなく、リスト ボックスに使用可能なドライブ文字を動的に表示できるスクリプトについて聞いたときは地域全体が興奮に包まれました。そのようなスクリプトが本当に存在するのでしょうか。では、確かめてみましょう。
<SCRIPT Language="VBScript">
Sub Window_onLoad
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk")
Set objDictionary = CreateObject("Scripting.Dictionary")
For Each objDisk in colDisks
objDictionary.Add objDisk.DeviceID, objDisk.DeviceID
Next
For i = 67 to 90
strDrive = Chr(i) & ":"
If objDictionary.Exists(strDrive) Then
Else
strDrives = strDrives & strDrive & vbCrLf
End If
Next
arrDrives = Split(strDrives, vbCrlf)
For Each strDrive In arrDrives
Set objOption = Document.createElement("OPTION")
objOption.Text = strDrive
objOption.Value = strDrive
AvailableDrives.Add(objOption)
Next
End Sub
</SCRIPT>
<body>
<select size="5" name="AvailableDrives" style="width:100">
</select>
</body>
このスクリプトとそのしくみについて説明する前に、このスクリプトはネットワーク ドライブを割り当てるための完全なスクリプトではないことをお知らせしておきます。このスクリプトは使用可能なドライブ文字を取得し、それらのドライブ文字をリスト ボックスに追加する方法を紹介することを目的としています。このコラムを書き終えるまでに完全なスクリプトをご紹介するつもりですが、今すぐにはお見せしません。何しろ、今すぐお見せすると言ったら、皆さんは、すぐにその箇所に進みたくなり、今日のコラムで説明するその他のすばらしい内容を読み飛ばしてしまうでしょうから。
注 : ちなみに、今日のコラムにすばらしい内容が含まれていると言っているわけではありません。でも念のためです。皆さんがすばらしい内容を読み飛ばしてしまうことは避けたいですから。 |
HTA 自体は非常に単純です。HTA は AvailableDrives という名前のリスト ボックスだけで構成されています。このリスト ボックスには、一度に 5 つの項目が表示され (そのために Size プロパティを指定しています)、リスト ボックスの幅は 100 ピクセルです。リスト ボックスを表示する HTML タグは次のとおりです。
<select size="5" name="AvailableDrives" style="width:100"> </select>
そして皆さんの思ったとおり、このリスト ボックスにはオプションがありません (Scripting Guys が職業を選択するときのような状態ですね)。でも心配しないでください。ご記憶のとおり、この HTA の趣旨はオプションをプログラムで追加することです。後ほど、この処理を行います。
注 : HTA、リスト ボックス、および <SELECT> ステートメントについて説明している内容がよくわからなくても大丈夫です。詳細については、HTA Developers Center を参照してください。 |
では、画面にリスト ボックスを表示することについての説明はこのくらいにして、リスト ボックスに使用可能なドライブ文字を動的に追加するすばらしい方法について説明しましょう。
この単純な HTA を開いたり、更新したりするたびに、HTA によって使用可能なドライブ文字が自動的に読み込まれるようにします。では、どのような方法で実行するのでしょうか。方法は簡単で、次のように、リスト ボックスを更新するコードを Window_onLoad という名前のサブルーチンに追加します。
<SCRIPT Language="VBScript">
Sub Window_onLoad
HTML プログラミングに詳しい方は、Web ページ (または HTA) が読み込まれるか更新されると必ず Window_onLoad サブルーチンが自動的に実行されることをご存知でしょう。HTML プログラミングに詳しくなくても問題ありません。今は、Web ページ (または HTA) が読み込まれるか更新されると必ず Window_onLoad サブルーチンが自動的に実行されることをご存知ですよね。
このサブルーチンでは、まずローカル コンピュータの WMI サービスに接続します。この HTA をリモート コンピュータに対しても実行できるか、ですって。そうですね、実行できる処理もあります。リモート コンピュータからドライブ文字に関する情報を取得するのは非常に簡単です。しかし、そのリモート コンピュータのドライブを割り当てるのはまったく別の作業で、この作業については今日のコラムでは説明しません (この作業のヒントについては、この Hey, Scripting Guy! のコラムを参照してください)。
WMI サービスに接続したら、ExecQuery メソッドを呼び出して、コンピュータで使用されているすべてのドライブ (ローカル ドライブと割り当てられたネットワーク ドライブの両方) に関する情報を返します。
Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk")
何ですって。ここまでの処理では、使用不可能なドライブ文字 (つまり、既に使用中のドライブ文字) のコレクションが返されるだけですが、必要なのは使用可能なドライブ文字を特定する処理だとおっしゃっているのでしょうか。うーん、これはちょっとしたジレンマですね。Scripting Guys はどうしたらこの状況から抜け出すことができるでしょうか。
これについても今日のコラムでは説明するつもりはありません (少なくとも、今すぐには説明しません)。まず、その説明の前に、Scripting.Dictionary オブジェクトのインスタンスを作成する必要があります。Dictionary オブジェクトのインスタンスを作成したら、次のコード ブロックを実行します。
For Each objDisk in colDisks
objDictionary.Add objDisk.DeviceID, objDisk.DeviceID
Next
このコードで行っている処理はおわかりでしょうか。ここでは、ディスク ドライブのコレクションをループ処理し、コレクション内のドライブごとにドライブ文字 (DeviceID プロパティの値) を Dictionary オブジェクトに追加しています。その際、ドライブ文字を Dictionary キーと項目の両方として使用しています。
注 : ええ、わかっています。でも、だれもが気に入っている (そして、2 番目にお気に入りの) スクリプト コラム「Sesame Script」(英語) では、Dictionary オブジェクトについて今まで知りたかったあらゆることを学習できます。 |
最終的に、次のようなキーを含む Dictionary オブジェクトが作成されます。各キーは使用されているドライブ文字を示します。
| • | C: |
| • | D: |
| • | E: |
| • | M: |
| • | X: |
そうです。使用可能なドライブ文字の一覧はまだ作成されていませんよね。でも、次のコード ブロックで状況は一変します。
For i = 67 to 90
strDrive = Chr(i) & ":"
If objDictionary.Exists(strDrive) Then
Else
strDrives = strDrives & strDrive & vbCrLf
End If
Next
心配しないでください。このコードのしくみについてちょうど説明しようとしていたところだったんです。まず、i の値が 67 から 90 までの間実行する For Next ループを設定します。67 から 90 までというのはなぜでしょうか。それは、ドライブ文字が C から Z までの文字に制限されているためです (A と B はフロッピー ドライブ用に予約されている文字です)。実際のところ、文字 C の ASCII 値は 67 で、文字 Z の ASCII 値は 90 です。i の値が 67 から 90 までの間実行する For Next ループを設定すると、C から Z までのすべての文字をループ処理できます。
For Next ループでは、Chr 関数を使用してループ変数の ASCII 値 (i) を実際の文字に変換します。たとえば、1 回目のループ処理では i の値は 67 になります。つまり、Chr 関数から文字 C が返されます。ASCII 値の変換後、文字の後にコロンを付け加えます。これを行うのが次のコード行です。
strDrive = Chr(i) & ":"
1 回目のループ処理の実行が終わったとき、strLine 変数には、C: という値が格納されています。
言われてみれば、ドライブ文字のように見えますね。
では、次の行に移りましょう。
If objDictionary.Exists(strDrive) Then
ここでは、Exists メソッドを使用して、最初のドライブ文字が Dictionary オブジェクトに含まれているかどうかを判断します。この条件を満たすとしましょう。つまり、最初のドライブ文字は既に使用されているので、ドライブの割り当てに使用できません。したがって、何の処理も行いません (ただし、ループ処理の先頭に戻って次の値で同じ処理を繰り返します)。
では、Dictionary オブジェクト内にドライブ文字が見つからないとします (つまり、Exists メソッドから False が返されたとします)。それが意味することはたった 1 つ、最初のドライブ文字は使用されていないので、ドライブの割り当てに使用できるということです。したがって、次のコード行を使用して、strDrives という名前の変数に最初のドライブ文字 (および復帰改行文字) を追加します。
strDrives = strDrives & strDrive & vbCrLf
おわかりでしょうか。ループ処理が完了するまでに、使用可能なすべてのドライブ文字が strDrives 変数に安全に格納されます。
これで、後は、使用可能なすべてのドライブ文字をリスト ボックスに追加する方法について説明するだけです。そのために、次の手順では Split 関数を使用して arrDrives という名前の配列を作成します。ご覧のとおり、ここでは復帰改行文字 (vbCrLf) で strDrive 変数の値を分割しています。
arrDrives = Split(strDrives, vbCrlf)
その結果、テスト コンピュータの次の項目で構成される配列が作成されます。これらの項目は、コンピュータで使用できるドライブ文字に見事に対応しています。
| • | F: |
| • | G: |
| • | H: |
| • | I: |
| • | J: |
| • | K: |
| • | L: |
| • | N: |
| • | O: |
| • | P: |
| • | Q: |
| • | R: |
| • | S: |
| • | T: |
| • | U: |
| • | V: |
| • | W: |
| • | Y: |
| • | Z: |
ここでようやく、次のコードを使用して、リスト ボックスに使用可能なドライブ文字を追加できます (スクリプトの処理は、説明するよりもはるかに早いので、ご心配なく)。
For Each strDrive In arrDrives
Set objOption = Document.createElement("OPTION")
objOption.Text = strDrive
objOption.Value = strDrive
AvailableDrives.Add(objOption)
Next
ここでは、For Each ループを設定して、使用可能なドライブ文字のコレクションに対して処理を繰り返しています。コレクション内の各項目に対して HTML の Option オブジェクトのインスタンスを作成します。各 Option オブジェクトはリスト ボックスの項目に相当します。これを行うのが次のコード行です。
Set objOption = Document.createElement("OPTION")
次に、以下の 2 行のコードを使用して、項目の Text プロパティと Value プロパティを構成します。
objOption.Text = strDrive objOption.Value = strDrive
HTML での作業に慣れていない方のために説明すると、Text プロパティにはリスト ボックスに表示されるただのテキストを指定します。また、Value プロパティには、特定のオプションを選択したときにサブルーチンに報告されるデータを指定します。オプションの Text プロパティと Value プロパティの値は同じでなくてもかまいません。ここでは、リスト ボックスに表示するドライブ文字と同じものをサブルーチンで使用する場合を想定して、Text プロパティと Value プロパティには同じ値を設定しました。
Option オブジェクトを構成したら、次のようにリスト ボックスに新しい項目を追加します。
AvailableDrives.Add(objOption)
ご覧のとおり、ここで行っているのは、リスト ボックス (AvailableDrives) を参照し、Add メソッドを呼び出しているだけです。その際、Option オブジェクトへのオブジェクト参照を唯一のパラメータとして Add メソッドに渡します。その後、ループ処理の先頭に戻り、配列内の次の項目についても同じ処理を繰り返します。ループ処理が完了すると、使用可能なすべてのドライブ文字がリスト ボックスに表示され、リスト ボックスは次のようになります。

さて、実際にドライブを割り当てる完全なスクリプトですね。詳しい説明は抜きにして、この処理を行うスクリプトをご紹介します。この HTA を使用するには、ドライブ文字を選択し、[ドライブの割り当て] ボタンをクリックします。ダイアログ ボックスが表示されたら、ネットワーク ドライブの UNC パス (たとえば、\\atl-fs-01\public) を入力します。[OK] をクリックすると、次の 2 つの処理が実行されます。
| • | ドライブの割り当て |
| • | リスト ボックスの更新 (以前使用できたドライブ文字が使用できなくなったため) |
コードは次のとおりです。
<SCRIPT Language="VBScript">
Sub Window_onLoad
Set objDictionary = CreateObject("Scripting.Dictionary")
strComputer = "."
Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2")
Set colDisks = objWMIService.ExecQuery("Select * from Win32_LogicalDisk")
For Each objDisk in colDisks
objDictionary.Add objDisk.DeviceID, objDisk.DeviceID
Next
For i = 67 to 90
strDrive = Chr(i) & ":"
If objDictionary.Exists(strDrive) Then
Else
strDrives = strDrives & strDrive & vbCrLf
End If
Next
arrDrives = Split(strDrives, vbCrlf)
For Each strDrive In arrDrives
Set objOption = Document.createElement("OPTION")
objOption.Text = strDrive
objOption.Value = strDrive
AvailableDrives.Add(objOption)
Next
End Sub
Sub MapDrive
If AvailableDrives.Value = "" Then
Msgbox "Please select a drive letter."
Exit Sub
End If
strDrive = AvailableDrives.Value
strPath = InputBox("Please enter the network path:")
If strPath = "" Then
Exit Sub
End If
Set objNetwork = CreateObject("Wscript.Network")
objNetwork.MapNetworkDrive strDrive, strPath
Location.Reload(True)
End Sub
</SCRIPT>
<body>
<select size="5" name="AvailableDrives" style="width:100px"></select><p>
<input type="button" value="ドライブの割り当て" onClick="MapDrive">
</body>
マリナーズの件についてですが、このコラムの読者でシアトル マリナーズ本部の方がいらっしゃったら (正直なところ、このコラムよりも『Baseball Digest』を読んでいると言ってくださった方が良いのですが)、scripter@microsoft.com (英語のみ) まで電子メールでオファーを提示していただければ、さいわいです。オファーの受け入れをお約束することはできません。というのも、国内を飛び回って野球をするためだけに、テクニカル ライタとしてのスリル満点でやりがいのある職を手放すことになるからです。しかし、確実に検討することはお約束します。