JScriptでWMI（その４）


## はじめに

「JScriptでWMI」も今回で４回目となりました。
こんなに、長くやる予定はなかったのですが、それなりにネタがあったのが驚きです。

今回は、イベントについてです。

個人的には、挙動がイマイチわかりづらいので、あまり利用しません。

## イベント待ち合わせ

「ExecNotificationQuery」メソッドを利用して、「SWbemEventSource」オブジェクトを取得し、「NextEvent」メソッドでイベントを待ち合わせを行います。

まずは、サンプルです。

```js
// クエリ設定
var query = "SELECT * FROM __InstanceCreationEvent WITHIN 5"
        + " WHERE TargetInstance ISA 'Win32_Process'"
        + " AND TargetInstance.Name = 'notepad.exe'";

// 「SWbemLocator」オブジェクト取得
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
// 「SWbemServices」オブジェクト取得（ローカルコンピュータ、名前空間「root\CIMV2」）
var services = locator.ConnectServer(null, "root\\CIMV2");
// 「SWbemEventSource」オブジェクト取得
var eventSource = services.ExecNotificationQuery(query);

// 無限ループ
while (true) {
    // イベント待ち
    var event = eventSource.NextEvent();
    // インスタンス取得
    var object = event.TargetInstance;
    // インスタンス存在チェック
    if (object != null) {
        // イベント結果
        WScript.Echo("Event:" + event.Path_.Class + ",ProcessId=" + object.ProcessId + ",Name=" + object.Name);
    }
}
```

「ExecQuery」メソッドと同じようにクエリ（WQL）を設定しますが、構文がだいぶ異なります。

FROM句に、イベントの種類`__InstanceCreationEvent`と、監視間隔`WITHIN 5`
WHERE句に、インスタンスの指定`TargetInstance ISA 'Win32_Process'`と、条件`TargetInstance.Name = 'notepad.exe'`
を指定します。

サンプルコードでは、「Win32_Process」クラスの「Name」が「notepad.exe」の物を、５秒間隔「WITHIN 5」で作られたら「__InstanceCreationEvent」イベントとして受信する。
といったクエリとなります。

監視間隔時間内に「notepad.exe」を起動→終了した場合は検知されません。

イベントの種類として、良く使うものを表にします。

|クラス名|説明|
|:--|:--|
|__InstanceOperationEvent|\__InstanceCreationEvent<br>\__InstanceModificationEvent<br>\__InstanceDeletionEvent<br>全て|
|__InstanceCreationEvent|インスタンスの追加時|
|__InstanceModificationEvent|インスタンスの変更時|
|__InstanceDeletionEvent|インスタンスの削除時|

### プロセスを監視したい場合

プロセスの追加、削除イベントを待ち合わせたい場合のサンプルコードです。

```js
// クエリ設定
var query = "SELECT * FROM __InstanceOperationEvent WITHIN 5"
        + " WHERE TargetInstance ISA 'Win32_Process'"
        + " AND TargetInstance.Name = 'notepad.exe'";

// 「SWbemLocator」オブジェクト取得
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
// 「SWbemServices」オブジェクト取得（ローカルコンピュータ、名前空間「root\CIMV2」）
var services = locator.ConnectServer(null, "root\\CIMV2");
// 「SWbemEventSource」オブジェクト取得
var eventSource = services.ExecNotificationQuery(query);

// 無限ループ
while (true) {
    // イベント待ち
    var event = eventSource.NextEvent();
    // インスタンス取得
    var object = event.TargetInstance;
    // インスタンス存在チェック
    if (object != null) {
        // イベント種類判定
        if (event.Path_.Class == "__InstanceCreationEvent"
            || event.Path_.Class == "__InstanceDeletionEvent") {
            // イベント結果
            WScript.Echo("Event:" + event.Path_.Class
                + ",ProcessId=" + object.ProcessId + ",Name=" + object.Name);
        }
    }
}
```

イベントの種類は`__InstanceOperationEvent`で、追加、変更、削除といづれの場合にも受信します。

イベント種類判定処理で、イベントクラスが`__InstanceCreationEvent`と`__InstanceDeletionEventだけイベント結果を出力しています。


### サービスを監視したい場合

サービスの変更イベントを受信するサンプルです。
監視間隔を10秒としています。

```js
// クエリ設定
var query = "SELECT * FROM __InstanceModificationEvent WITHIN 10"
        + " WHERE TargetInstance ISA 'Win32_Service'"
        + " AND TargetInstance.Name = 'wuauserv'";

// 「SWbemLocator」オブジェクト取得
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
// 「SWbemServices」オブジェクト取得（ローカルコンピュータ、名前空間「root\CIMV2」）
var services = locator.ConnectServer(null, "root\\CIMV2");
// 「SWbemEventSource」オブジェクト取得
var eventSource = services.ExecNotificationQuery(query);

// 無限ループ
while (true) {
    // イベント待ち
    var event = eventSource.NextEvent();
    // インスタンス取得
    var object = event.TargetInstance;
    // インスタンス存在チェック
    if (object != null) {
        // イベント結果
        WScript.Echo("Event:" + event.Path_.Class
            + ",State=" + object.State + ",Name=" + object.Name);
    }
}
```

## その他のイベント待ち合わせ

いままで「__InstanceOperationEvent」「__InstanceCreationEvent」「__InstanceModificationEvent」「__InstanceDeletionEvent」などを利用していましたが、他にもイベント待ちのWMIクラスがあります。

その内の一つ、プロセス監視を使ってみます。

|クラス名|説明|
|:--|:--|
|Win32_ProcessTrace|Win32_ProcessStartTrace<br>Win32_ProcessStopTrace<br>全て|
|Win32_ProcessStartTrace|プロセス追加|
|Win32_ProcessStartTrace|プロセス削除|

```js
// クエリ設定
var query = "SELECT * FROM Win32_ProcessTrace WHERE ProcessName = 'notepad.exe'";

// 「SWbemLocator」オブジェクト取得
var locator = new ActiveXObject("WbemScripting.SWbemLocator");
// 「SWbemServices」オブジェクト取得（ローカルコンピュータ、名前空間「root\CIMV2」）
var services = locator.ConnectServer(null, "root\\CIMV2");
// 「SWbemEventSource」オブジェクト取得
var eventSource = services.ExecNotificationQuery(query);

// 無限ループ
while (true) {
    // イベント待ち
    var event = eventSource.NextEvent();
    // イベント結果
    WScript.Echo("Event:" + event.Path_.Class
        + ",ProcessId=" + event.ProcessID
        + ",Name=" + event.ProcessName);
}
```

インスタンスが取得出来るわけではなく、イベントのプロパティのみ使えます。
このサンプルの場合「Win32_ProcessTrace」を利用しているので「Win32_ProcessStartTrace」「Win32_ProcessStopTrace」両方のイベントを待ち合わせます。

## 懸念点

冒頭にも書きましたが、個人的にあまり利用しません。
その大きな理由は以下の二点です。

* 間隔指定なので、取りこぼす場合がある<br>（だったら、自前でポーリングする）
* プロセスの正常な終了方法がわからない<br>（リソースが宙ぶらりんにならないか心配）