JScriptによるWMIの利用
Windowsで仕事用の(つまり、Rubyとかを勝手にインストールできないときの)スクリプトを書くときは大抵JScriptで書いているんだけど、世の中はVBScriptが主流らしく、サンプルコードとかが手に入りづらかったりする。最近もレジストリ操作をしようとして悩んでしまった。
たとえばWMIのレジストリ操作機能を使って、HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control キー配下の WaitToKillServiceTimeout の値を読みたいとする*1。VBScriptなら、サンプルはいくらでも転がっているし、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に乗り換えたほうがいいんだろうか……とちょっと思った。