WMIコネタ ― 2008年06月18日 03時12分05秒
chkconfigモドキとかpsモドキとかで久々にWMIに触ったらなんだか面白かったのでちょろちょろといじくっているのだが、そのときに見つけたりしたコネタをば。正確には前も調べたけどすっかり忘れてたってとこなんだが。
まずはEnumeratorを拡張するが吉
WQLで問い合わせた結果はいわゆる「コレクション」が返ってくる。VBSの場合は何も考えずFor Eachを使えるのだが、JScriptの場合はEnumeratorでくるんでやる必要がある。まぁWMIに限らずWSH/HTAでCOMを触る場合はそうなるのだが。
んで好みの問題だと思うけど、WMIみたいなメタデータ中心のオブジェクトを扱う場合はコレクションのループ処理を多用するハメになるので、Enumeratorでeachなんかが使えるように拡張しといたほうが便利かな、と。
最低限のサンプル。
Enumerator.prototype = function(iterator) { var i = 0; for(this.moveFirst(); ! this.atEnd(); this.moveNext()) { iterator( this.item(), i++ ); } };このままだとprototype.jsみたいにcontinue/breakの制御ができないけど、まぁコレクションを一通りまわすことはできるのでこれだけでも結構快適になる。
後で触れるけど、WMIのデータそのままだとちょっと具合が悪い型もあるので、これにmapとtoArrayでも加えればいいんじゃないかと。
Enumerator.prototype.map = function(iterator) { var results = []; this.each( function(item, index) { results[ results.length ] = iterator( item, index ); } ); return results; }; Enumerator.prototype.toArray = function() { return this.map( function(item) { return item; } ); };
datetime型
例えばWin32_ProcessクラスのCreationDateプロパティなんかがこの「datetime」型なのだが、JScriptで取り回すと次のような文字列に暗黙で変換される。
var wmi = new ActiveXObject("WbemScripting.SWbemLocator").ConnectServer(); var query = "SELECT * FROM Win32_Process WHERE Name = 'firefox.exe'"; var firefox = new Enumerator(wmi.ExecQuery(query)).toArray()[0]; if( firefox ) { WSH.Echo( firefox.CreationDate ); // '20080618004435.514465+540' }要するに「年月日時分秒.マイクロ秒GMT時差」という形式。なので、こんな感じでDateにすり合わせてやるとよいかと。
var reg = /^(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\.(\d+)(.\d+)$/; var parts = reg.exec( firefox.CreationDate ); WSH.Echo( new Date( parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parseInt( parts[7] / 1000 ) ) ); // → 'Fri Jul 18 00:44:35 UTC+0900 2008'こんなゴミゴミした正規表現じゃなくてsubstrとかでもいいんだけども。
値が配列なプロパティの扱い方
例えばWin32_NetworkAdapterConfigurationクラスのDefaultIPGatewayプロパティやIPAddressプロパティなんかがそうなのだが、値が配列の場合がある。
こういったWMIの配列は、そのままJScriptのArrayに変換されないらしい。
query = "SELECT IPAddress FROM Win32_NetworkAdapterConfiguration"; var nics = new Enumerator(wmi.ExecQuery(query)).toArray(); // 先頭のNICにIPアドレスが割りあたってるとして、 WSH.Echo( typeof(nics[0].IPAddress) ); // → unknownしかもこの unknownオブジェクト、Enumeratorに食わせることもできない。
new Enumerator( nics[0].IPAddress ); // → 'オブジェクトがコレクションではありません'例外こういう場合はVBArrayを使うんだそうな。
VBArrayってのはEnumerator同様JScript独特のオブジェクトで、
Visual Basic のセーフ配列へアクセスする方法を提供します。ってなオブジェクトだそうな。幸いなことに(というか、目的考えると当たり前か)toArray()でJScriptのArrayに変換することができる。
ってわけで、
WSH.Echo( new VBArray( nics[0].IPAddress ).toArray().join("\n") ); // → IPアドレスがバインドされてる数だけ出力されるVBArrayをEnumerableにするってのもよいかも。今度やってみよう。
ちなみにProperty_で動的列挙をする場合はIsArrayプロパティがtrueになっているので、それで判別できるかと。
query = "SELECT * FROM Win32_NetworkAdapterConfiguration"; nic = new Enumerator(wmi.ExecQuery(query)).toArray()[0]; new Enumerator(nic.Properties_).each( function(prop) { WSH.Echo( [ "プロパティ名:", prop.Name, "\t", "データ型:", prop.CIMType, "\t", "配列?:", prop.IsArray ].join( "" ) ); // 例(一部) // プロパティ名:MACAddress データ型:8 配列?:false // プロパティ名:IPAddress データ型:8 配列?:true } );
Win32_Processのメモ ― 2008年06月17日 03時48分07秒
前のエントリで書いた「ps.js」は、基本的にWMIでWin32_Processを列挙してそこから得られる情報を出力しているのだが、CPU時間とオーナーはちとてこずったのでその辺のメモをば。
まず、Win32_Processオブジェクトのメンバーについては、以下を参照。
CPU時間
Win32_Processのプロパティの中で、名前に「Time」がついているのは「KernelModeTime」と「UserModeTime」の2つ。そんで、どちらのプロパティも解説をみると
- 64ビット 符号なし整数
- 100ナノ秒単位
例えば「vmware-authd.exe」というプロセスが、CPU時間「0:13:36」という表示になっているとして、このプロセスをWQLで検索して、KernelModeTime/UserModeTimeの各プロパティ値を1000万で割ってみると、
プロパティ | 値 | 値 / 1000万 |
---|---|---|
KernelModeTime | 5390451088 | 539.0451088 |
UserModeTime | 2814146544 | 281.4146544 |
最初は「ミリ秒単位にしてDateに食わせりゃあんまり計算しなくていいかも」とか不精なことを考えていたのだが、GMT時差補正や24時間を越えた場合の展開、getMinutes()やgetSeconds()などをちくちく呼び出すことを考えたら、素直に計算したほうが早いことに気づいたのでベタで計算することにした。
ちなみにuint64にそのまま合わせるデータ型がないためか、JSでKernelModeTimeなどを取得した場合、返ってきたデータの型はstringだったので、そこらへんも注意。
オーナー情報の取得
プロパティをざっと眺めたところ、それっぽいやつはなかったのだが、メソッドのほうには「GetOwner」「GetOwnerSid」とそれっぽいやつがあった。
「GetOwnerSid」はSIDを取得するのだろうから、今回は用なしとした。そんな値みてもすぐにはピンと来ないし、第一長すぎるし。
なので、「GetOwner」メソッドの詳細を見てみたところ、
なんて定義になっていた。outパラメータで値を取得し、メソッドの戻り値は成否を示すらしい。uint32 GetOwner( [out] string User, [out] string Domain );
って、JSでoutパラメータなんでどうやって取り回すんだ??
ためしに
// proc には Win32_Processのインスタンスが入ってるとして、 var user, domain; var result = proc.GetOwner( user, domain ); if( result == 0 ) { // success WSH.Echo( "user = " + user + "\ndomain = " + domain ); }とかやってみたが、result == 0 の場合でもuser/domainとも値が入っていない。まぁ予想はしてたけど。
この説明ページにあるVBSのサンプルコードは
Return = objProcess.GetOwner(strNameOfUser)とか普通にやってるのだがJSのサンプルないし。さて、困った。
んで「Win32_Process getowner jscript」なんてベタキーワードでググってみたら見つかりましたよ、こんなのが。
なるほど、ExecMethod_なんてリフレクションメソッドを使うと、outパラメータの値も戻り値に含まれるのね。ちなみにGetOwner()を普通に呼び出したときの戻り値は上記「xout」の「ReturnValue」プロパティに入るらしい。var q = "SELECT * FROM Win32_Process"; var e = New Enumerator( GetObject( "winmgmts:{impersonationLevel=impersonate}!//./root/cimv2" ).ExecQuery( q ) ); WScript.Echo("User\tPID"); for ( ; e.atEnd(); e.moveNext() ) { var x = e.item(); var ometh = x.Methods_.Item("GetOwner"); var xout = x.ExecMethod_(ometh.Name, null); var user = xout.User; var domain = xout.Domain; WScript.Echo(user + "\t" + x.ProcessId ); }
で、これを参考に
var info = proc.ExecMethod_( proc.Methods_.Item("GetOwner").Name, null );なんてコードを書いたんだけど、よくよく考えてみると「proc.Methods_.Item("GetOwner").Name」って普通に"GetOwner"を返すので、
var info = proc.ExecMethod_( "GetOwner", null );でぜんぜん問題なかったんだけどね(あ、第二引数もいらないかも)。
オマケ・現在のログオンユーザ情報の取得
ps.jsでは、-aオプションなしの時はオーナーが現在のログオンユーザのプロセスに限定するようにしたんだけども、その「現在のログオンユーザ」の取得はWMIとはまったく別口で取得。
こっちはきわめて簡単で、WScript.NetworkオブジェクトのUserDomain/UserNameプロパティでOK。オマケにComputerNameでNetBIOS名も取れる。
var net = new ActiveXObject("WScript.Network"); WSH.Echo( [ "user = " + net.UserName, "domain = " + net.UserDomain, "machine = " + net.ComputerName ].join("\r\n") );
ps.jsとps.cmd / kill.cmd ― 2008年06月17日 00時54分14秒
なんちゃってコマンド 第四弾
我ながらしつこいなと思いつつ「なんちゃってコマンド」スクリプト。今度はps作ってみました。
ま、WMIでWin32_Process列挙できればそんなに難しくないので、似たようなことやってる人はいるだろなぁ。
例によってソース
ちょっと長いけど例によってのっけときます。NYSLです。
var fso = new ActiveXObject("Scripting.FileSystemObject"); var shell = new ActiveXObject("WScript.Shell"); // コマンドライン引数の取得 var args = new function() { var args = WSH.Arguments, result = []; for(var col = new Enumerator( args ); ! col.atEnd(); col.moveNext()) { result[ result.length ] = String( col.item() ); } var named = {}; for(var col = new Enumerator( args.Named ); ! col.atEnd(); col.moveNext()) { named[ String( col.item() ) ] = String( args.Named.Item( col.item() ) ); } var unnamed = []; for(var col = new Enumerator( args.Unnamed ); ! col.atEnd(); col.moveNext()) { unnamed[ unnamed.length ] = String( col.item() ); } result.named = named; result.unnamed = unnamed; result.toArray = function() { var result = []; for(var i = 0, l = this.length; i < l; i++) { result[ i ] = /((^".*"$)|(^'.*'$))/.test( this[i] ) ? this[i] : [ '"', this[i], '"' ].join(""); } return result; }; result.toString = function() { return this.toArray().join(" "); }; return result; }(); // cscriptで強制起動 if( /wscript\.exe$/i.test( WSH.FullName ) ) { shell.Run( [ "cscript", /((^".*"$)|(^'.*'$))/.test( WSH.ScriptFullName ) ? WSH.ScriptFullName : [ '"', WSH.ScriptFullName, '"' ].join(""), args ].join(" ") ); WSH.Quit(); } // ユーティリティ関数定義 var echo = function(s) { print( [ s, "\n" ].join("") ); } var print = function(s) { WSH.StdOut.Write( s || "" ); } var input = function() { if( arguments[0] ) print( arguments[0] ); print( ">" ); return WSH.StdIn.ReadLine(); } Error.prototype.toString = function() { return this.description || this.message || this.number || this; }; var $break = {}; var $continue = {}; Enumerator.prototype.each = function(iterator) { try { var i = 0; for(this.moveFirst(); ! this.atEnd(); this.moveNext()) { try { iterator( this.item(), i++ ); } catch(e) { if( e != $continue ) throw e; } } } catch(e) { if( e != $break ) throw e; } }; String.prototype.repeat = function(count) { var buf = []; for(var i = 0; i < count; i++) buf[buf.length] = this; return buf.join(""); }; String.prototype.align = function(align, size) { if( ! /^((left)|(right)|(center))$/i.test( align ) ) align = "left"; if( isNaN(size) || size < 1 ) size = this.length; switch( align ) { case "left": return [ this, " ".repeat(size) ].join("").substr(0, size); case "right": return [ " ".repeat(size), this ].join("").slice(-1 * size); default: var pad = " ".repeat( size ); var s = [ pad, this, pad ].join(""); return s.substr( parseInt(s.length / 2) - parseInt(size / 2), size ); } }; Number.prototype.toTime = function() { var sec = this % 60; var min = Math.floor( this / 60 ); var hour = Math.floor( min / 60 ); min = hour ? min % 60 : min; return [ hour, ( "00" + min ).slice(-2), ( "00" + sec ).slice(-2) ].join(":"); }; var ProcMgr = function() { this.wmi = new ActiveXObject("WbemScripting.SWbemLocator").ConnectServer(); var wshNet = new ActiveXObject("WScript.Network"); this.currentUser = [ wshNet.UserDomain, wshNet.UserName ].join("\\"); }; ProcMgr.prototype = { list : function(id) { var query = "SELECT * FROM Win32_Process"; query += id != null ? [ " WHERE ProcessId = ", id ].join("") : ""; return new Enumerator( this.wmi.ExecQuery(query) ); }, kill : function(id) { this.list(id).each( function(proc) { proc.Terminate(); throw $break; } ); }, exec : function(params) { if( params[0] && params[0].toLowerCase() == "--kill" && params[1] ) { // --kill this.kill( params[1] ); } else if( params[0] && params[0].toLowerCase() == "--list" ) { // --list // -a オプション。全ユーザのプロセスを列挙 var allUser = ( params[1] != null && /^-.*A/i.test( params[1] ) ); // -d オプション。コマンドラインの情報を付加 var detail = ( params[1] != null && /^-.*d/i.test( params[1] ) ); var inited = false; var _self = this; this.list().each( function(proc) { if( ! inited ) { inited = true; echo( [ "PID".align( "right", 5 ), "PPID".align( "right", 5 ), "OWNER".align( "left", 16 ), "WK-SIZE".align( "right", 8 ), "PEAK".align( "right", 8 ), "TIME".align( "right", 10 ), "CMD".align( "left", 16 ) ].join(" ") ); } var cmd = proc.CommandLine == null ? "(null)" : String( proc.CommandLine ); var time = parseInt( ( Number(proc.KernelModeTime) + Number(proc.UserModeTime) ) / 10000 / 1000 ); var info = proc.ExecMethod_( proc.Methods_.Item("GetOwner").Name, null ); // プロセスオーナーのチェック if( ! allUser && [ info.Domain, info.User ].join("\\").toLowerCase() != _self.currentUser.toLowerCase() ) throw $continue; echo( [ String(proc.ProcessId).align( "right", 5 ), String(proc.ParentProcessId).align( "right", 5 ), String(info.User || "(null)" ).align( "left", 16 ), String(Number(proc.WorkingSetSize) / 1024).align( "right", 8 ), String(Number(proc.PeakWorkingSetSize) / 1024).align( "right", 8 ), String( time.toTime() ).align( "right", 10 ), [ String(proc.Name), detail ? [ "(", cmd, ")" ].join("") : "" ].join(" ") ].join(" ") ); } ); } else { // no parameter -> --list this.exec( [ "--list" ] ); } } }; new ProcMgr().exec( args );今回のダウンロードはzip形式にしてます。こちらからどうぞ。
使い方
今回は不精して、1つのスクリプトで2つのコマンドを兼用しています。おかげでjsファイルとしての呼び出しはちと不自然な感じなんですが。
プロセスの列挙 - psコマンド風味
何も引数をつけずに起動すると psコマンドっぽく(?)稼動中のプロセスを列挙します。ただし、標準入力での入力待ちを行わないので、明示的にcscriptで起動したほうがよいでしょう。
C:\ps_command\scripts>cscript ps.js Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved. PID PPID OWNER WK-SIZE PEAK TIME CMD 472 1532 dara-j 2072 4912 0:01:01 tp4serv.exe 2172 2112 dara-j 29992 95644 0:42:34 explorer.exe 2272 2172 dara-j 4384 6808 0:00:30 TSVNCache.exe : (中略) : 1640 2172 dara-j 2840 2884 0:00:00 cmd.exe 4920 1640 dara-j 6612 6612 0:00:00 cscript.exe C:\ps_command\scripts>列の意味は、左から
- PID - プロセスID
- PPID - 親プロセスID
- OWNER - プロセスのオーナー
- WK-SIZE - ワーキングセット サイズ
- PEAK - ピークメモリサイズ
- TIME - CPU時間(カーネル+ユーザ)
- CMD - コマンドイメージ名
また、第一引数に「--list」をつけた場合も上と同じ動作になります。
C:\ps_command\scripts>cscript ps.js --list Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved. PID PPID OWNER WK-SIZE PEAK TIME CMD 472 1532 dara-j 2072 4912 0:01:01 tp4serv.exe 2172 2112 dara-j 29928 95644 0:42:39 explorer.exe 2272 2172 dara-j 4384 6808 0:00:30 TSVNCache.exe : (中略) : 1640 2172 dara-j 2840 2884 0:00:00 cmd.exe 5864 2172 dara-j 1888 6716 0:00:01 taskmgr.exe 5212 1640 dara-j 6604 6604 0:00:00 cscript.exe C:\ps_command\scripts>
--listで起動した場合はさらに第二引数にオプションをつけることができます。
「-a」オプションをつけると、他のオーナーが所有するプロセスも列挙します。といってもだいたい「SYSTEM」や「NETWORK SERVICE」くらいでしょうが。
C:\ps_command\scripts>cscript ps.js --list -a Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved. PID PPID OWNER WK-SIZE PEAK TIME CMD 0 0 (null) 16 0 73:50:31 System Idle Process 4 0 SYSTEM 36 2040 0:05:51 System 836 4 SYSTEM 44 480 0:00:00 smss.exe : (中略) : 1932 2172 dara-j 2848 2864 0:00:00 cmd.exe 4696 1932 dara-j 6624 6624 0:00:00 cscript.exe 3356 1168 NETWORK SERVICE 5728 5728 0:00:00 wmiprvse.exe C:\ps_command\scripts>
これは個人的な趣味でつけたのですが、「-d」オプションをつけると、起動時のコマンドライン(引数まで込み)を出力します。
C:\ps_command\scripts>cscript ps.js --list -d Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved. PID PPID OWNER WK-SIZE PEAK TIME CMD 472 1532 dara-j 2072 4912 0:01:02 tp4serv.exe ("C:\Progr am Files\Lenovo\TrackPoint\tp4serv.exe") 2172 2112 dara-j 30244 95644 0:43:04 explorer.exe (C:\WINDO WS\Explorer.EXE) 2272 2172 dara-j 4408 6808 0:00:30 TSVNCache.exe ("C:\Pro gram Files\TortoiseSVN\bin\TSVNCache.exe") : (中略) : 5864 2172 dara-j 2632 6716 0:00:10 taskmgr.exe (C:\WINDOW S\system32\taskmgr.exe) 1932 2172 dara-j 5516 5592 0:00:00 cmd.exe ("cmd.exe" /K cd C:\ps_command) 2260 1932 dara-j 6624 6624 0:00:00 cscript.exe (cscript p s.js --list -d) C:\ps_command\scripts>複数インスタンス起動できるエディタなんかの、あるインスタンスを特定する場合なんかに便利かなとか思ってつけたのですが、正直表示が見づらいです。まぁオマケ機能くらいの感じで。
実行例は示しませんが、「-a」と「-d」は「-ad」といった具合で同時に指定できます。
プロセスの強制終了 - killコマンド風味
第一引数に「--kill」をつけるとkillモードで起動します。この呼び出し方法は第二引数にプロセスIDを必ず指定する必要があります。
以下はメモ帳(notepad.exe)を起動し、プロセスIDを確認後にkillモードで終了させる例です。
C:\ps_command\scripts>notepad C:\ps_command\scripts>cscript ps.js | findstr notepad 5728 1932 dara-j 4888 4888 0:00:00 notepad.exe C:\ps_command\scripts>cscript ps.js --kill 5728 Microsoft (R) Windows Script Host Version 5.6 Copyright (C) Microsoft Corporation 1996-2001. All rights reserved. C:\ps_command\scripts>cscript ps.js | findstr notepad C:\ps_command\scripts>
バッチファイル
見てきたように2種類の動作を1つのスクリプトに集約したため、ちょっと引数が気持ち悪いのですが、バッチファイルを添付してあるので勘弁してください(ダウンロードファイルをzipにしたのはこのため)。
ダウンロードファイルを解凍すると、以下のような構造になっています。
- ps_command/
- scripts/
- ps.js - スクリプト本体
- kill.cmd - killモード起動用のバッチファイル
- ps.cmd - psモード起動用のバッチファイル
- scripts/
バッチファイルはスクリプトパスを「"%~dP0\scripts\ps.js"」としているため、「ps_command」フォルダごとリロケータブルになっています。「%~dp0」がなんなのかはこちらのサイトなんかが参考になるのでは。
ps.cmdは見れば即わかるくらいシンプルですが、kill.cmdは引数なしの時にusageを表示する分岐処理を入れてます。まぁぜんぜん難しいことしてるわけではないのですが。
その他
なんというか、できそうだからやってみました、以上のことがないようなネタですが。本当はvmstatとかfreeとかもやろうかと思ったけど、まんまlinuxと同じ情報があるわけでなし、なにを出力するか考えるのも面倒になってきたのでなんちゃってシリーズはこれでおしまいかな。
ま、そうはいっても今回はCPU時間とかオーナー情報の取得でちとてこずったので、そこらへんを別記事にまとめる予定。
chkconfig.js ― 2008年06月15日 06時19分04秒
またくだらんモノ作ったなぁ
くだらんと思いつつも、chkconfig風にWindowsサービスの起動方法を確認したり変更したりするユーティリティスクリプト作ってみました。
いや、前回のservice.cmdの引数に渡す「サービス名」を確認するのが主な目的だったんだけど、起動方法の変更とかもなんとなくできそうだったのでやってみたらできたってことで。ソース
こんな感じ。例によってNYSLで。ダウンロードはこちらから。
var fso = new ActiveXObject("Scripting.FileSystemObject"); var shell = new ActiveXObject("WScript.Shell"); // コマンドライン引数の取得 var args = new function() { var args = WSH.Arguments, result = []; for(var col = new Enumerator( args ); ! col.atEnd(); col.moveNext()) { result[ result.length ] = String( col.item() ); } var named = {}; for(var col = new Enumerator( args.Named ); ! col.atEnd(); col.moveNext()) { named[ String( col.item() ) ] = String( args.Named.Item( col.item() ) ); } var unnamed = []; for(var col = new Enumerator( args.Unnamed ); ! col.atEnd(); col.moveNext()) { unnamed[ unnamed.length ] = String( col.item() ); } result.named = named; result.unnamed = unnamed; result.toArray = function() { var result = []; for(var i = 0, l = this.length; i < l; i++) { result[ i ] = /((^".*"$)|(^'.*'$))/.test( this[i] ) ? this[i] : [ '"', this[i], '"' ].join(""); } return result; }; result.toString = function() { return this.toArray().join(" "); }; return result; }(); // cscriptで強制起動 if( /wscript\.exe$/i.test( WSH.FullName ) ) { shell.Run( [ "cscript", /((^".*"$)|(^'.*'$))/.test( WSH.ScriptFullName ) ? WSH.ScriptFullName : [ '"', WSH.ScriptFullName, '"' ].join(""), args ].join(" ") ); WSH.Quit(); } // ユーティリティ関数定義 var echo = function(s) { print( [ s, "\n" ].join("") ); } var print = function(s) { WSH.StdOut.Write( s || "" ); } var input = function() { if( arguments[0] ) print( arguments[0] ); print( ">" ); return WSH.StdIn.ReadLine(); } Error.prototype.toString = function() { return this.description || this.message || this.number || this; }; var $break = {}; var $continue = {}; Enumerator.prototype.each = function(iterator) { try { var i = 0; for(this.moveFirst(); ! this.atEnd(); this.moveNext()) { try { iterator( this.item(), i++ ); } catch(e) { if( e != $continue ) throw e; } } } catch(e) { if( e != $break ) throw e; } }; String.prototype.repeat = function(count) { var buf = []; for(var i = 0; i < count; i++) buf[buf.length] = this; return buf.join(""); }; var SvcMgr = function() { this.wmi = new ActiveXObject("WbemScripting.SWbemLocator").ConnectServer(); }; SvcMgr.prototype = { list : function(name) { var query = "SELECT Name, DisplayName, Description, StartMode, State FROM Win32_Service"; if( name ) query += " WHERE Name = '" + name + "'"; return new Enumerator( this.wmi.ExecQuery( query ) ); }, change : function(name, value) { this.list(name).each( function(service) { service.ChangeStartMode( value ); throw $break; } ); }, exec : function(params) { if( params[0] && params[0].toLowerCase() == "--list" ) { // --list var max = 39; this.list( params[1] || false ).each( function(service) { echo( [ service.Name.length < max ? ( service.Name + " ".repeat(max) ).substr(0, max) : service.Name, service.StartMode ].join(" ") ); } ); } else if( params[0] && params[0].toLowerCase() == "--detail" && params[1] ) { // --detail this.list( params[1] ).each( function(service) { echo( "サービス名 : " + service.Name ); echo( "表示名 : " + service.DisplayName ); echo( "スタートアップの種類 : " + ( { "Boot" : "ブート", "System" : "システム", "Auto" : "自動", "Manual" : "手動", "Disabled" : "無効" }[ service.StartMode ] || service.StartMode ) ); echo( "状態 : " + ( { "Running" : "実行中", "Stopped" : "停止中" }[ service.State ] || service.State ) ); echo( "説明 : " ); echo( " " + ( service.Description || "(no description)" ) ); echo( "" ); throw $break; } ); } else if( params[0] && ! /^--/.test(params[0]) && params[1] ) { // change startmode var mode = { "auto" : "automatic", "sys" : "system", "man" : "manual", "dis" : "disabled" }[ params[1] ] || params[1]; this.change(params[0], mode); this.exec( [ "--list", params[0] ] ); } else { echo( "使い方 : [cscript | wscript] chkconfig.js --list [サービス名]" ); echo( " [cscript | wscript] chkconfig.js --detail サービス名" ); echo( " [cscript | wscript] chkconfig.js サービス名 [ Boot |" ); echo( " System |" ); echo( " Automatic |" ); echo( " Manual |" ); echo( " Disabled ]" ); echo(); } } }; new SvcMgr().exec( args.unnamed );これをcscript経由で起動するバッチファイルを%SystemRoot%にでも入れておけばよいかと。
使い方
そのまま起動すると使い方は表示されるのでだいたいわかると思いますが、以下の3種類の引数を受け付けます。
- --list [サービス名]
- <サービス名> <起動方法>
- --detail <サービス名>
--list [サービス名]
「--list」はサービスの起動方法を確認します。こんな感じ。
C:\Documents and Settings\dara-j>cscript chkconfig.js --list AcPrfMgrSvc Auto AcSvc Disabled Alerter Disabled ALG Disabled AppMgmt Manual aspnet_state Manual AudioSrv Auto : (中略) : WudfSvc Manual WZCSVC Auto xmlprov Manual C:\Documents and Settings\dara-j>各行はサービス名と起動方法が出力されます。
ここに表示されるサービス名は管理ツールの「サービス」で表示される「表示名」ではなく、内部のサービス名で、前回のservice.cmdに(というか、net startやnet stopに)渡すサービス名はこれになります。
--listの後ろにサービス名をつけると、そのサービスの情報のみが表示されます。
C:\Documents and Settings\dara-j>cscript chkconfig.js --list iisadmin IISADMIN Auto C:\Documents and Settings\dara-j>また、パイプでfindstrにつないでもいいかもしれません。
C:\Documents and Settings\dara-j>cscript chkconfig.js --list | findstr -i vnc winvnc Auto C:\Documents and Settings\dara-j>findstrは-i(Ignore Case)がよいでしょう。サービス名うろ覚え時にぜひ。
<サービス名> <起動方法>
サービスの起動方法を変更します。Linuxのようなランレベルの概念はないため、--level <levels>のようなパラメータは必要なく、on/off/resetの代わりに「Boot」「System」「Automatic」「Manual」「Disabled」の5種類の指定が可能です(が、「Boot」と「System」はよくわかりません。指定しても反映されてるやらされてないやら)。
実行すると、設定反映後の状態を出力します。
C:\Documents and Settings\dara-j>cscript chkconfig.js w3svc disabled W3SVC Disabled C:\Documents and Settings\dara-j>cscript chkconfig.js w3svc automatic W3SVC Auto C:\Documents and Settings\dara-j>ちなみに大文字小文字は区別されません。
--detail <サービス名>
これは本家chkconfigには相当する機能はないのですが、オマケで実装してみました。指定サービスの詳細を表示します。
C:\Documents and Settings\dara-j>cscript chkconfig.js --detail iisadmin サービス名 : IISADMIN 表示名 : IIS Admin スタートアップの種類 : 自動 状態 : 実行中 説明 : インターネット インフォメーション サービスのスナップインを使用した Web と FTP サービスの管理を提供します。 Enter キーで終了します> C:\Documents and Settings\dara-j>
ちょっとした解説
たいしたことはしてないのですが、WMIでWin32_Serviceクラスを扱っています。サービスの列挙は
SELECT * FROM Win32_Serviceってのが基本で(実際は取得するプロパティを限定してますが)、WHERE句でサービスを限定したり、返ってきたインスタンスに対してChangeStartModeメソッドを実行して起動方法を変えたりしています。
参考リンク
wget.jsを修正しました ― 2008年06月13日 21時12分46秒
保存先に同名ファイルがあった場合に上書き保存に失敗するというなさけないバグがあったので、あわてて修正しました。元記事のソース・ダウンロード用ソースとも修正ずみです。
最近のコメント