JScriptによるWMIの利用

Windowsで仕事用の(つまり、Rubyとかを勝手にインストールできないときの)スクリプトを書くときは大抵JScriptで書いているんだけど、世の中はVBScriptが主流らしく、サンプルコードとかが手に入りづらかったりする。最近もレジストリ操作をしようとして悩んでしまった。

たとえばWMIのレジストリ操作機能を使って、HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control キー配下の WaitToKillServiceTimeout の値を読みたいとする*1VBScriptなら、サンプルはいくらでも転がっているし、MSDNでもすぐ見つかる。では、JScriptではどうか?

レジストリを操作するための StdRegProv インスタンスは以下のように取得できる。

var oLocator = new ActiveXObject("WbemScripting.SWbemLocator");
var oService = oLocator.ConnectServer(".", "root\\default");
var oRegProv = oService.Get("StdRegProv");

で、StdRegProvクラスのドキュメント を見ると、GetStringValue という REG_SZ 型の値を取り出すためのメソッドがあるのがわかる。これを呼べばいいだろう。…と思いきや、このメソッドの定義は以下のようになっているのだった。

uint32 GetStringValue(
  [in]  uint32 hDefKey = 2147483650,
  [in]  string sSubKeyName,
  [in]  string sValueName,
 [out]  string sValue
);

見ての通り返値は数値で、メソッドが成功したかどうかを示す。肝心のレジストリの値は sValue というoutパラメータ(参照渡し)で返されるらしい。でも、JScript/JavaScriptにそんな機能あったっけ? JScriptのリファレンス を眺めてもそんな呼び出し方法はないし、適当に

var sValue;
oRegProv.GetStringValue(0x80000002, "SYSTEM\\CurrentControlSet\\Control",
  "WaitToKillServiceTimeout", out sValue);

とやっても当然だめ。outがなくてもだめ。「JScript 参照渡し」とか「JScript "call by reference"」で検索してもなかなか解決策は見つからず(探し方が悪いだけかも)。結局、Writing WMI Scripts in JScript という有益なページを見つけるまで、小一時間くらいかかってしまった…。

正解はこちら。なんと直接は呼び出せず、リフレクションのようなものを使わないといけないらしい。

var oMethod = oRegProv.Methods_.Item("GetStringValue");

var oInParam = oMethod.InParameters.SpawnInstance_();
oInParam.hDefKey = 0x80000002; // HKEY_LOCAL_MACHINE
oInParam.sSubKeyName = "SYSTEM\\CurrentControlSet\\Control";
oInParam.sValueName = "WaitToKillServiceTimeout";

var oOutParam = oRegProv.ExecMethod_(oMethod.Name, oInParam);

if (oOutParam.ReturnValue == 0) {
  WScript.Echo(oOutParam.sValue);
} else {
  WScript.Echo("Error! return value = " + oOutParam.ReturnValue);
}

sValue というプロパティ名は GetStringValue の場合で、たとえば GetDWORDValue なら uValue という名前になったりする*2

WMIやJScriptについては体系立てて勉強したことがないので、上の方法はこの世界では常識なのかもしれないけど、この情報量の少なさを体感してしまうとやっぱりVBScriptに乗り換えたほうがいいんだろうか……とちょっと思った。

*1:これだけならWshShellのRegReadメソッドを使った方が早い。本当はもっと別のことをやろうとした。

*2:入力パラメータ、出力パラメータとも、メソッドのリファレンスに載っている仮引数名と同じ?