2005/09/08

Tales from the Script: 2003 年 9 月 - スクリプト環境の最適化

Tales from the Script: 2003 年 9 月 - スクリプト環境の最適化
スクリプトを使ってスクリプト環境を調査する 私 たちは、たくさんの新しいスクリプトテクノロジを一方的に提供しただけだということと、Microsoft で自分たちが唱えていることを実践している、すなわち、「自前のドッグフードを食べている」という信念から、(ローカルコンピュータ上で実行するための) 問題の解決を支援するスクリプトを提供しています。このスクリプトは、コンピュータ上にインストールされているオペレーティングシステムと、そのシステム に対応した最新の WSH、WMI、ADSI の各バージョンを教えてくれます。最新でないものが見つかった場合は、後述する「テクノロジのダウンロード」で、最新バージョンのダウンロードができる URL を参照してください。 このコラムのガイド的スクリプトである Scriptenv.vbs は、複数のアプローチを使ってさまざまな種類の表示データを検索します。このスクリプトは、各タスクを実行するための個別の関数を作成します。また、複雑 なスクリプトを管理しやすいように分割するための手段を VBscript (および、ほとんどすべてのスクリプト言語) で作成します。 既定のスクリプト ホストの識別 初 心者用の GetWshHost() 関数は、現在の既定のスクリプト ホストが Wscript.exe と Cscript.exe のどちらであるかを判別します。この関数は、Wscript の FullName プロパティから取得した文字列 (スクリプトホストとしての実行可能ファイルのフル パスが返されます) を解析します。 C:\Windows\System32\CScript.exe VBScript の Right() 関数と LCase() 関数を使用すると、文字列全体を小文字に変換 (検索時に文字の種類を限定するため) して、文字列の最後から 11 文字分を抽出することができます。この 11 文字が既定のスクリプトホストのファイル名に相当します。 以下に示す必要最小限のコードが、関数 (名前をすべて小文字に変換する LCase() 関数を除きます) の中核を形成します。 strFullName = WScript.FullName strWshHost = Right(strFullName, 11) WScript.Echo "Default script host: " & strWshHost ChangeToCscript サブルーチンは、既定のスクリプト ホストが Wscript であるかどうかをチェックします。もしそうであれば、このサブルーチンは一見おかしな行動を取りますが、実際には有効です。サブルーチンは、新しいウィン ドウを開き、それ自身 (Scriptenv.vbs) の新しいインスタンスを起動します。今回は、Cscript 下で実行しているため、続いて、オリジナルのバージョンを終了させます (Wscript との違いは、最初のインスタンスが終了するまで有効にならない点です)。なぜでしょうか?説明しましょう。Wscript でスクリプトを実行するということは、情報を読むためにおよそ 10 個のポップアップ ウィンドウで [OK] をクリックする必要があることを意味します (そして、もちろん、メッセージボックスをクリックするたびに、データが画面から消えてしまいます)。そのために、スクリプトは CScript 下で動作することを自分自身に強要します。こうすれば、メッセージボックスの大群に襲われることもなく、スクリプトが終了しても、すべてのデータがコマン ドウィンドウに表示されたままになります。この処理を行うコードは次のようになります。 If strWshHost = "wscript.exe" Then Set objShell = CreateObject("WScript.Shell") objShell.Run _ "%comspec% /k ""cscript //h:cscript&&cscript scriptenv.vbs""", _ MAXIMIZE_WINDOW If Err.Number <> 0 Then WScript.Echo "Error 0x" & hex(Err.Number) & " occurred. " & _ Err.Description & ". " & VbCrLf & _ "Could not temporarily change the default script host to Cscript." Err.Clear WScript.Quit End If WScript.Quit End If メモ 使用しているスクリプト ホストに関係なく、スクリプト内で使用するオブジェクトは WScript と呼ばれます。 コンピュータにインストールされている Windows のバージョンの識別 コ ンピュータ上で動作しているオペレーティング システムとそのバージョンを調べたい場合は、[マイ コンピュータ] の [コントロール パネル] から [システムのプロパティ] を開いて [全般] タブの右上で確認することができます。でも、もしスクリプトの中でこの情報を使用したい場合はどうすればいいのでしょうか? メモ なぜ、コンピュータで動作しているオペレーティング システムとそのバージョンを知りたいのでしょうか?例を挙げて説明しましょう。あなたは、WSH 5.6 を Windows 98 が動作しているコンピュータと Windows 2000 が動作しているコンピュータの両方にインストールすることができます。しかし、Windows 98 が動作しているコンピュータ上に WSH 5.6 をインストールするためのセットアップ ファイルは、Windows 2000 マシン用のものとは異なります。したがって、手の込んだアップグレードを実施する前に、対象のオペレーティング システムを確認しておく必要があります。 WMI は、オペレーティング システムの名前、バージョン、サービス パックだけでなく、インストールの日付、言語、シリアル番号を含むその他の 50 を超えるプロパティが取得できる Win32_OperatingSystem クラスを提供しています。 画面上のオペレーティング システムの名前とバージョンの表示を簡略化するために、以下のコードを使用できます。 Set objWMIService = GetObject("winmgmts:" & _ "{impersonationLevel=impersonate}!\\.\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("SELECT * FROM Win32_OperatingSystem") For Each objOperatingSystem in colOperatingSystems Wscript.Echo "OS Name: " & objOperatingSystem.Caption Wscript.Echo "Version: " & objOperatingSystem.Version Next CScript 下でこのスクリプトを実行すると、以下のような情報が返されます。 OS Name: Microsoft Windows XP Professional Version: 5.1.2600 こ のコード サンプルは、GetObject を呼び出して、Win32_OperatingSystem クラスが存在する WMI の root\cimv2 namespace にバインドし、ExecQuery メソッドとそのパラメータとして WMI Query Language (WQL) クエリ (SQL に似ている) を使ってコンピュータ上のすべてのオペレーティングシステムに関する情報を取得してから、その名前とバージョン番号を画面に表示します。 Scriptenv.vbs は、同様のコードを GetOSVer() という名前の関数にラップします。この関数は、オペレーティング システムを一意に表す整数をスクリプトの本体に返します。 た だし、Win32_OperatingSystem は、古いバージョンのオペレーティングシステムに対する一意の番号は提供しません。そのため、Scriptenv.vbs は、後からスクリプトの中で使用する整数 intOSVer を生成するために、OSType と Version の 2 つのプロパティとネストした Select Case 命令文を使用します。Select Case は、扱いが容易な If … Then … Else If … Else … End If よりも、多くの基準に基づいて判定する VBScript コマンドです。このコードの簡略バージョンを以下に示します。 Set objWMIService = GetObject("winmgmts:" & _ "{impersonationLevel=impersonate}!\\.\root\cimv2") Set colOperatingSystems = objWMIService.ExecQuery _ ("Select * from Win32_OperatingSystem") For Each objOperatingSystem In colOperatingSystems intOSType = objOperatingSystem.OSType strOSVer = Left(objOperatingSystem.Version, 3) Next Select Case intOSType Case 16 'Windows 95 intOSVer = 1 Case 17 'Windows 98 intOSVer = 2 Case 18 Select Case strOSVer Case 4.0 intOSVer = 4 'Windows NT 4.0 Case 5.0 intOSVer = 5 'Windows 2000 Case 5.1 intOSVer = 6 'Windows XP Case 5.2 intOSVer = 7 'Windows Server 2003 Case Else intOSVer = 0 'Older or newer version End Select Case Else intOSVer = 0 'Older or newer version End Select メモ Win32_OperatingSystem.OSType と Win32_OperatingSystem.Version は、Windows Millennium Edition に関する値を提供しません。 次に、このスクリプトは、WSH、WMI、および ADSI のバージョンを取得して、特定のオペレーティング システム対応の最新バージョンと比較します。 コンピュータにインストールされている WSH、WMI、ADSI のバージョンの識別 GetWSHVer 関数は、オペレーティング システムのバージョンを含む整数と既定のスクリプト ホスト (現在は Cscript) の名前を含む文字列をパラメータに取ります。この関数は、WSH の組み込みプロパティを使用して、既定のスクリプト ホスト、スクリプトホストへのパス、および WSH の Version プロパティと BuildVersion プロパティを表示します。それから、これらのプロパティとオペレーティング システムを比較して、WSH のバージョンがオペレーティングシステム対応の最新版かどうかを判断し、そうである場合は、True の Boolean 値を、そうでない場合は、False の Boolean 値を返します。エラー処理と比較コードを省略すると、この関数の中心部分は以下のようになります。 WScript.Echo _ "WSH Path: " & WScript.FullName & VbCrLf & _ "WSH Version: " & WScript.Version & VbCrLf & _ "WSH Build Version: " & WScript.BuildVersion & VbCrLf GetWMIVer 関数は、GetWSHVer 関数と同様のアプローチを使用して WMI のバージョンをチェックします。ただし、この関数は、オペレーティングシステムのバージョンを表す整数だけをパラメータとして取ります。 GetWMIVer は、Win32_WMISetting クラスを使用して、マシン上の WMI 設定のコレクションを取得します (これは 1 つしか存在しません)。そして、ビルド バージョンと既定の WMI スクリプト名前空間を表示します。それから、If … Then … Else ..End If 命令文と Boolean ロジックツリーを使って、クラスの BuildVersion プロパティとオペレーティングシステムのバージョンを表す整数を比較して、そのバージョンが最新かどうかを判断します。結果に応じて、True または False を返します。 Scripting Guys ヒント 確かに、これまでのすべてのケースにおいて、バージョン 番号を報告し、すべてのデータが読まれるまで表示したままにして、最新かつ最高のスクリプトテクノロジがインストールされているかどうかを判断することが できました。しかし、なぜ、スクリプトにあなた専用の動作をさせないのでしょうか?たとえば、すべてのコンピュータまたは少なくとも 256 MB 以上の RAM を搭載したすべてのコンピュータに新しいアプリケーションをインストールしたいと仮定します。RAM 値の集まり (128、256、128、512、1024、256) だけを返すのではなく、なぜ、スクリプトに、RAM が 256 MB 以上の場合は "十分なメモリ"、RAM が 256 MB 未満の場合は "不十分なメモリ" のような報告をさせないのでしょうか?これには、若干余分なコーディングが必要ですが、実行後の分析にかかる時間を節約できます (あなたがスクリプトを書いて、他の人がそれを実行する場合に特に有効です)。 以下のコードの断片から基本的な WMI 情報を入手することができます。 Set objWMIService = GetObject _ ("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2") Set colWMISettings = objWMIService.ExecQuery _ ("Select * from Win32_WMISetting") For Each objWMISetting In colWMISettings Wscript.Echo _ "WMI Build Version: " & _ objWMISetting.BuildVersion & vbCrLf & _ "Default scripting namespace: " & _ objWMISetting.ASPScriptDefaultNamespace Next ADSI のバージョンをチェックする関数 GetADSIVer は、従来の同様の関数よりも複雑な動作をします。この関数は、組み込み WshShell オブジェクトを使用して、2 つのレジストリエントリを読み出し、その結果から ADSI のバージョンを推測します。そして、ADSI 名前空間プロバイダを使用して、インストールされている ADSI プロバイダ、他のプロトコルの名前空間へのアクセスを可能にするモジュール、および LDAP や NDS のようなディレクトリサービスのリストを取得します。最後に、GetWMIVer と同様の Boolean ロジック ツリーを使用して、ADSI のバージョンとオペレーティング システムのバージョンを比較し、True または False を返します。基本的なコードを以下に示します。 Set objShell = CreateObject("WScript.Shell") strAdsiVer = _ objShell.RegRead("HKLM\SOFTWARE\Microsoft\Active Setup\Installed " & _ "Components\{E92B03AB-B707-11d2-9CBD-0000F87A369E}\Version") If strAdsiVer = vbEmpty Then strAdsiVer = _ objShell.RegRead("HKLM\SOFTWARE\Microsoft\ADs\Providers\LDAP") If strAdsiVer = vbEmpty Then strAdsiVer = "ADSI is not installed." Else strAdsiVer = "2.0" End If ElseIf Left(strAdsiVer, 3) = "5,0" Then If intOSVer = 5 Then strAdsiVer = "5.0.2195" ElseIf intOSVer = 6 Then strAdsiVer = "5.1.2600" ElseIf intOSVer = 7 Then strAdsiVer = "5.2.3790" End If End If WScript.Echo "ADSI Version: " & strAdsiVer & VbCrLf If strAdsiVer <> "ADSI is not installed." Then Set colProvider = GetObject("ADs:") Wscript.Echo "ADSI Providers" & VbCrLf & _ "--------------" For Each objProvider In colProvider Wscript.Echo objProvider.Name Next Wscript.Echo End If 最 後に、ListUpToDate サブルーチンは、GetWSHVer、GetWMIVer、GetADSIVer から返された Boolean 値をパラメータに取って、それぞれをネストされた If … Then … Else ループに入力します。各ループは、テクノロジのバージョンが "オペレーティング システムにとって最新である" または "もっと新しいバージョンをインストールする必要がある" のどちらかを報告します。