スクリプティング・ガイ

先日普段採り上げないWMIネタを振ったのは仕事で急に(本当に急にだ)以下のようなプログラムを作れと要求されたからだ。

指定された文字列パターンを入力して、それに合致するインストールされたソフトウェア又はファイルがローカルPC上に存在するかどうかを確認せよ。

こんなのわざわざ.NET CLRJava VM、はたまたEXEにやらせる必要も無い。ということでWindows Scriptの出番と相成った訳だ。(現在であればPowershell等のほうがスマートなのだろうが、全てのPCにインストールされている事が期待できるのはWSH位しかない)

さて、要件だがパターンファイルはテキストの各行が正規表現の要素になっていることを期待して、それを読み込めば良いことにしよう。

var objFSO = WScript.CreateObject("Scripting.FileSystemObject");
var objPatFile = objFSO.OpenTextFile("prohiblist.pat", 1);

var patStr= objPatFile.ReadLine();
while(!objPatFile .AtEndOfStream) {
    patStr= patStr+ "|" + objPatFile.ReadLine();
}
objPatFile.Close();
var exp = new RegExp(patStr); 

全てのファイルシステムの列挙はWMIのCIM_DataFileクラスのクエリを使う。

var objWMIService = GetObject("winmgmts:\\\\.\\root\\CIMV2");
var colFiles = objWMIService.ExecQuery("Select Name, Path from CIM_DataFile");
var enumItems = new Enumerator(colFiles);
var match = 0;
for (; !enumItems.atEnd(); enumItems.moveNext()) {
    var objFile = enumItems.item();
    if ( objFile.Name.match(prohibPattern)) {
        WScript.Echo("該当するファイルが見つかりました : "
              + objFile.Name + "\t" + objFile.Path);
    }
}

んで、インストールベースは昨日採り上げたWin32_Productクラスに対するクエリを使う。

var colFiles = objWMIService.ExecQuery("Select Caption, Description, InstallLocation from Win32_Product");
var enumItems = new Enumerator(colFiles);
for (; !enumItems.atEnd(); enumItems.moveNext()) {
    var objFile = enumItems.item();
    if ( objFile.Caption.match(prohibPattern) ||
        objFile.Description.match(prohibPattern) ) {
        WScript.Echo("該当するソフトウェアが見つかりました : "
                 + objFile.Caption + "\t" +objFile.InstallLocation);
    }
}

普段書かないので正直、さっぱりだ。

この手のスクリプトはネット上のサンプルを探すのが速いのだがその9割がVBSで書かれており、JSの例は殆ど無い。なもので最初はVBSで書いていたのだがやっぱり気持ち悪くてJSで書き直してしまった。

サンプルに関してはやはりここだな。Scripting Guyは素晴らしい。
Hey, Scripting Guy!
Hey, Scripting Guy!(邦訳)

追記:
Windows VistaでWin32_Productクラスが取れない件に関しては社本さんにコメントでアドバイス頂いた。当面はMSI APIを直接叩いて?実現することとして、Win32_Productクラスが取得できないのはバグなのだろうか。(直す予定があるのだろうか)