Windows マシンで特定のプロセスがいつ開始または停止されるかを監視する必要があります。現在、WMI システムを利用して 5 秒ごとにクエリを実行していますが、WMI は WMI であるため、5 秒ごとに CPU スパイクが発生します。これを行うより良い方法はありますか?実行中のプロセスのリストを作成し、System.Diagnostics 名前空間を介してそれらに Exited イベントをアタッチすることはできますが、作成するイベント ハンドラーはありません。
4 に答える
終了/クリーンアップ時にイベントから適切に分離できなかった場合に、WMI イベントをリッスンしているときに CPU スパイクが発生しました。WMI イベント サブスクリプションを "リーク" していないかどうかを確認することをお勧めします。念のため、できるだけ早くイベントから切り離し、常にそうするようにしてください。
さらに詳しく説明するために、PSEventing ライブラリを使用して WMI イベントをリッスンするPowerShell ブックの例を次に示します。
Add-PSSnapin PSEventing -ErrorAction SilentlyContinue
$queryString = @' SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32_Service' AND TargetInstance.Name = 'w3svc' AND TargetInstance.State = 'Stopped' '@
$query = New-Object System.Management.WQLEventQuery ` -argumentList $queryString
$watcher = New-Object System.Management.ManagementEventWatcher($query)
Connect-EventListener ウォッチャー EventArrived
$watcher.Start()
echo "W3CSVC サービスが停止するのを待っています..." Get-Event -wait |
foreach {
Write-Host -foreground 赤 "W3SVC サービスが停止しました!" }$watcher.Stop()
切断-EventListener ウォッチャー EventArrived
エコー「完了」
スクリプトの終了時にDisconnect-EventListenerビットを実行しないと、イベントにアタッチする 3 回目または 4 回目に CPU スパイクが発生します。私の推測では、システムはまだイベントを配信しようとしています。
プロセスの PID/名前のみを探している場合は、代わりに "SELECT * FROM Win32_ProcessTrace WHERE TargetInstance.ProcessName = 'name'" if applied* などの WQL クエリを使用して、Win32_ProcessTrace イベントを取得することができます。
「SELECT * FROM __InstanceModificationEvent WITHIN 10 WHERE TargetInstance ISA 'Win32Process' AND TargetInstance.Name = 'name'」を使用することの落とし穴は、バックエンドでの動作にあります。%windir%\system32\wbem\logs ディレクトリ内の wbemess.log を調べると、次のログが表示されます (__InstanceDeletionEvent を使用)。
(Wed Jul 22 13:58:31 2009.73889577): 10 以内の __InstanceDeletionEvent からクエリを選択して通知シンクを登録しています。 (Wed Jul 22 13:58:31 2009.73889577): 10 以内の __InstanceDeletionEvent からクエリを選択してフィルター 047209E0 をアクティブにします。 (Wed Jul 22 13:58:31 2009.73889577): クエリ select * を使用してフィルター 0225E560 をアクティブにします。 (Wed Jul 22 13:58:31 2009.73889577): フィルタ 'select * from __ClassOperationEvent where TargetClass isa "Win32_Process"' をプロバイダ $Core でアクティブ化 (Wed Jul 22 13:58:31 2009.73889587): フィルター 'select * from __InstanceDeletionEvent を 10 以内にアクティブ化します。ここで、TargetInstance ISA 'Win32_Process'' はプロバイダー $Core で (Wed Jul 22 13:58:31 2009.73889587): イベントクエリ select * from __InstanceDeletionEvent を満たすために、ポーリングクエリ select * from Win32_Process を導入しています。ここで、TargetInstance ISA 'Win32_Process' (Wed Jul 22 13:58:31 2009.73889587): 名前空間 '//./root/CIMV2' でポーリング クエリ 'select * from Win32_Process' を実行しています (Wed Jul 22 13:58:31 2009.73889697): ポーリング クエリ 'select * from Win32_Process' が完了しました (Wed Jul 22 13:58:41 2009.73899702): 名前空間 '//./root/CIMV2' でポーリング クエリ 'select * from Win32_Process' を実行しています (Wed Jul 22 13:58:41 2009.73899792): ポーリング クエリ 'select * from Win32_Process' が完了しました
ご覧のとおり、リモート マシンでの実際のイベントの実装は、WITHIN 句の値で指定された間隔で Win32_Process に対してクエリを実行することです。その結果、そのポーリング内で開始および停止するプロセスは、イベントを発生させません。
WITHIN 句を小さな値に設定して、この影響を最小限に抑えることができますが、より良い解決策は、常に発生する Win32_ProcessTrace のような真のイベントを使用することです。
*MSDN によると、Win32_ProcessTrace が機能するには、クライアント マシンで Windows XP、サーバー マシンで Windows 2003 が必要です。古い OS を使用している場合、__InstanceModificationEvent クエリの使用に行き詰まる可能性があります。
これは、実際の世界で行う方法とは異なりますが、役立つはずです。これは私のCPUをあまり動かしていないようです。
static void Main(string[] args)
{
// Getting all instances of notepad
// (this is only done once here so start up some notepad instances first)
// you may want use GetProcessByPid or GetProcesses and filter them as required
Process[] processesToWatch = Process.GetProcessesByName("notepad");
foreach (var process in processesToWatch)
{
process.EnableRaisingEvents = true;
process.Exited +=
(s, e) => Console.WriteLine("An instance of notepad exited");
}
Thread watchThread = new Thread(() =>
{
while (true)
{
Process[] processes = Process.GetProcesses();
foreach (var process in processes)
{
Console.WriteLine("{0}:{1}", process.Id, process.ProcessName);
}
// Don't dedicate a thread to this like I'm doing here
// setup a timer or something similiar
Thread.Sleep(2000);
}
});
watchThread.IsBackground = true;
watchThread.Start();
Console.WriteLine("Polling processes and waiting for notepad process exit events");
Console.ReadLine();
}