JScriptでWMI（その１）

## はじめに

Windows Management Instrumentation (WMI) のプロパティ値を取得してみます。

以前、[JScriptでODBCのドライバ一覧を表示](http://qiita.com/tnakagawa/items/0941a0e02d2cb17dc9a4)
で利用した、「WbemScripting.SWbemLocator」を利用します。



## WMI値取得

WMIのプロパティを取得するメソッドを作成します。
結果を連想配列の配列として返します。
また、要素が配列の場合は配列で設定します。

```js
function wmiProps(query) {
    // 結果
    var result = [];
    try {
        // 「SWbemLocator」オブジェクト取得
        var locator = new ActiveXObject("WbemScripting.SWbemLocator");
        // 「SWbemServices」オブジェクト取得（ローカルコンピュータ、名前空間「root\CIMV2」）
        var services = locator.ConnectServer(null, "root\\CIMV2");
        // クエリ実行、「SWbemObjectSet」オブジェクト取得
        var set = services.ExecQuery(query);
        // 「SWbemObjectSet」をJScriptで扱えるように「Enumerator」に変換
        var enumSet = new Enumerator(set);
        // 「SWbemObjectSet」の最後までループ
        while (!enumSet.atEnd()) {
            // 要素「SWbemObject」を取得
            var item = enumSet.item();
            // プロパティ「SWbemPropertySet」取得
            var props = item.Properties_;
            // 「SWbemPropertySet」をJScriptで扱えるように「Enumerator」に変換
            var enumProps = new Enumerator(props);
            // アイテム変数
            var item = {};
            // プロパティ分ループ
            while (!enumProps.atEnd()) {
                var val = null;
                // プロパティ取得
                var prop = enumProps.item();
                // nullチェック
                if (prop.Value != null) {
                    // 配列判定
                    if (prop.IsArray) {
                        // 配列化
                        val = new VBArray(prop.Value).toArray();
                    } else {
                        val = prop.Value;
                    }
                }
                // アイテム設定
                item[prop.Name] = val;
                // 次のプロパティへ移動
                enumProps.moveNext();
            }
            // アイテム設定
            result.push(item);
            // 次の要素へ移動
            enumSet.moveNext();
        }
    } catch (e) {
        // エラーの場合
        throw e;
    }
    return result;
}
```

WMIのプロパティで取得される配列はJScriptでそのまま解釈できない為
「VBArray」を利用して、配列にします。

```js
val = new VBArray(prop.Value).toArray();
```

### 変換

配列をそのまま出力すると、見づらい為、CSV形式に変換するメソッドを用意します。
要素が配列の場合は、改行（CRLF）で区切ります。

```js
function props2csv(props) {
    var csv = "";
    // キー配列
    var keys = null;
    // プロパティ配列数分ループ
    for (var i = 0; i < props.length; i++) {
        // １行分の要素取得
        var items = props[i];
        // キー存在チェック
        if (keys == null) {
            // 初期化
            keys = [];
            // 要素のキー取得
            for (key in items) {
                keys.push(key);
            }
            // キーをヘッダに設定
            csv += keys.join(",") + "\r\n";
        }
        // １行分のデータ
        var line = [];
        // 要素数分ループ
        for (var j = 0; j < keys.length; j++) {
            // 要素取得
            var item = items[keys[j]];
            // 要素存在チェック
            if (item != null) {
                // 要素配列チェック
                if (item instanceof Array) {
                    // 配列の場合、改行で連結
                    item = item.join("\r\n");
                } else {
                    // 文字列化
                    item = "" + item;
                }
                // ダブルコーテーションフラグ偽
                var isQuote = false;
                // 要素にダブルコーテーションの存在チェック
                if (item.indexOf("\"") >= 0) {
                    // ダブルコーテーションを置き換え
                    item = item.replace(/\"/g, "\"\"");
                    // ダブルコーテーションフラグ真
                    isQuote = true;
                } else if (item.indexOf(",") >= 0
                    || item.indexOf("\r") >= 0
                    || item.indexOf("\n") >= 0) {
                    // 要素に、「,」「CR」「LF」のいづれかがあるので
                    // ダブルコーテーションフラグ真
                    isQuote = true;
                }
                // ダブルコーテーションフラグ判定
                if (isQuote) {
                    // 両端をダブルコーテーションで囲む
                    item = "\"" + item + "\"";
                }
            }
            // 要素設定
            line.push(item);
        }
        // １行分のデータを設定
        csv += line.join(",") + "\r\n";
    }
    return csv;
}
```


## 利用例

私が良く使うWMIの情報をいくつか挙げます。

### プロセス一覧取得

プロセスの一覧を取得する場合、WMIの「Win32_Process」を参照します。

```js
var query = "SELECT * FROM Win32_Process";
var props = wmiProps(query);
var csv = props2csv(props);
WScript.Echo(csv);
```

私が良く使うのは、以下の項目となります。

|名前|説明|
|:--|:--|
|Caption|簡単な説明文、イメージ名|
|ProcessID|プロセスID、PID|
|WorkingSetSize|メモリ量、ワーキングセット（メモリ）|

「*」で全体を取得するのではなく、項目指定の場合は以下のようになります。

```js
var query = "SELECT Caption,ProcessID,WorkingSetSize FROM Win32_Process";
var props = wmiProps(query);
var csv = props2csv(props);
WScript.Echo(csv);
```

### ローカル記憶領域情報取得

ローカルディスクの情報は、「Win32_LogicalDisk」を参照します。

```js
var query = "SELECT * FROM Win32_LogicalDisk";
var props = wmiProps(query);
var csv = props2csv(props);
WScript.Echo(csv);
```

私が良く使うのは、以下の項目となります。

|名前|説明|
|:--|:--|
|Caption|簡単な説明文、ドライブ名|
|FileSystem|ファイルシステム|
|FreeSpace|空き容量|
|Size|全体の容量|

「*」で全体を取得するのではなく、項目指定の場合は以下のようになります。
また、タイプ「DriveType」はローカルドライブだけを指定したい為
WHERE句にローカルディスク「3」を指定します。

```js
var query = "SELECT Caption,FileSystem,FreeSpace,Size FROM Win32_LogicalDisk WHERE DriveType = '3'";
var props = wmiProps(query);
var csv = props2csv(props);
WScript.Echo(csv);
```

### ネットワークアダプター情報取得

ネットワークの情報は、「Win32_NetworkAdapterConfiguration」を参照します。

```js
var query = "SELECT * FROM Win32_NetworkAdapterConfiguration";
var props = wmiProps(query);
var csv = props2csv(props);
WScript.Echo(csv);
```

私が良く使うのは、以下の項目となります。

|名前|説明|
|:--|:--|
|Description|説明|
|IPAddress|IPアドレス|
|IPSubnet|サブネットマスク|
|MACAddress|物理アドレス|

「*」で全体を取得するのではなく、項目指定の場合は以下のようになります。
また、TCP/IPがバインドされていて有効なアダプターのみを指定したい為
WHERE句として「IPEnabled」に「true」を指定します。

```js
var query = "SELECT Description,IPAddress,IPSubnet,MACAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = true";
var props = wmiProps(query);
var csv = props2csv(props);
WScript.Echo(csv);
```