【C#】あるプロセスの開始と終了のイベントを取得する

なんでかわかりませんが、最近WMIばっかりやってる気がします。まぁ仕事だからやるしかないし、案外面白いので別にいいんですが、資料が全然なかったり、ばらばらだったり、VBSでのやり方しか見つからなかったり、いきなりPowerShellの話をされたり、中々しんどいですね。

さて、WMIにはイベントと言う概念があります。こいつを捕まえることでプロセスやサービスが立ち上がった時に何かしたり、CPUの使用値がn%を超えた時に何かしたりとか、そんなことが出来ます。

概念としてはこのあたりが参考になると思います。

一番参考になるのはスクリプトセンターの記事ですかね。実際にWQLで何をやってるのかはこちらの記事がわかりやすいです。このサイト、恐ろしいほどに情報量が多く、WMIの各クラスのメンバやメソッドの翻訳なんかも置いてあるので、やりたいことが決まったら大体こことStackOverFlowだけで何とかなります。

C#でのイベントの取得方法はこれです。非同期で取得する場合はこっちです。説明とサンプルコードを読めばざっくりと理解できると思います。サンプルコードではManagementEventWatcherコンストラクタWqlEventQueryを渡していますが、ManagementEventWatcherに普通に文字列としてWQLを受け取るコンストラクタがあるので、クエリをコピペするならこっちの方が取り回しやすいかもです。

で、「あるプロセスの開始と終了のイベントを取得する」方法なんですが、__InstanceCreationEventと__InstanceDeletionEventを使うことで可能です。

イベントを取得するManagementEventWatcherを作る

それじゃあ早速ManagementEventWatcherを作ってみましょう。

var procName = "notepad.exe";
var pollingTime = 10;
var query = new StringBuilder();

query.AppendLine("SELECT")
     .AppendLine("*")
     .AppendLine("FROM")
     .AppendLine("__InstanceCreationEvent")
     .AppendFormat("WITHIN {0}", pollingTime).AppendLine()
     .AppendLine("WHERE")
     .AppendLine("TargetInstance ISA 'Win32_Process'")
     .AppendFormat("AND TargetInstance.Name = '{0}'", procName);

var watcher = new ManagementEventWatcher(@"root\CIMV2", query.ToString());

これでメモ帳が起動したかどうかを10秒ごとにチェックするManagementEventWatcherが出来ました。終了を通知して欲しいなら、FROM句を__InstanceDeletionEventに書き換えるだけでOKです。次にイベントを受け取った時の処理を書いてみましょう。

イベントを受け取った後の処理を記述する

同期版

using (var watcher = new ManagementEventWatcher(@"root\CIMV2", query.ToString()))
{
    //ManagementEventWatcher.Option.Timeoutでタイムアウト時間を設定
    watcher.Options.Timeout = TimeSpan.FromSeconds(30);

    var e = watcher.WaitForNextEvent();
    Console.WriteLine("{0} has been started.", ((string)((ManagementBaseObject)e["TargetInstance"])["Name"]));

    watcher.Stop();
}

非同期版

using (var watcher = new ManagementEventWatcher(@"root\CIMV2", query.ToString()))
{

    watcher.EventArrived += (sender, e) =>
    {
        var newEvent = e.NewEvent;
        Console.WriteLine("{0} has been started.", ((string)((ManagementBaseObject)newEvent["TargetInstance"])["Name"]));
    };

    watcher.Start();

    System.Threading.Thread.Sleep(30000);

    watcher.Stop();
}

こんな感じです。キモは「受け取ったイベントのTargetInstanceプロパティをManagementBaseObjectにキャストすることで、実際にイベントを起こしたプロセスのプロパティにアクセス出来る」ってところですかね。

まとめ

WMI面白いのでみんなもマスターしましょう。