【極限の知見】ポーリングを捨て、イベントを待て。WMIを活用した「真のリアルタイム監視」への昇華
こんにちは。現場の最前線で、複雑怪奇なシステムの自動化と最適化を司るチーフアーキテクトです。
「VBScriptで、特定のプロセスのCPU使用率が高くなったら通知するプログラムを作りたい」
もしあなたがそう考えているなら、まずは今持っている「やり方」を一度疑ってみてください。多くの人が最初に思いつくのは、`Do While` ループの中で `WMI` を参照し、`WScript.Sleep` で少し休む……という「ポーリング(周期的な問い合わせ)」という手法です。
もちろん、それは間違いではありません。しかし、プロフェッショナルの世界では、それは「力技」と呼ばれます。数秒おきに「どうですか?」「まだですか?」と問いかけ続けるのは、サーバーのリソースを無駄に消費し、監視の遅延を生む、エレガントとは言い難い設計です。
今日は、そんな「初心者レベルのループ処理」を脱却し、Windowsの深層にあるWMI(Windows Management Instrumentation)のイベント通知機能を使いこなし、システムに負荷をかけずに「変化の瞬間」を捉える、極限の監視スクリプトを伝授しましょう。
ここをクリアすれば、あなたはもう「マクロを記録する人」ではなく、「システムを制御するエンジニア」への第一歩を踏み出すことになります。
1. 「ポーリング」と「イベント駆動」の決定的な違い
技術的な詳細に入る前に、概念を整理しておきましょう。ここが設計思想の根幹です。
ポーリング(Polling)の限界
ポーリングとは、「自ら能動的に確認しに行く」スタイルです。
- 動き: `ループ開始` → `CPU確認` → `Sleep(5秒)` → `ループ開始`…
- 欠点:
- 5秒間、何も起きていなくてもCPUをわずかに使い続ける。
- 5秒の間に異常が発生しても、次の確認まで検知できない(タイムラグ)。
- 監視対象が増えると、スクリプト自体の負荷が指数関数的に増える。
イベント駆動(Event-Driven)の美学
イベント駆動とは、「変化が起きた時に、システムから通知を受ける」スタイルです。
- 動き: `監視設定を登録` → `(スクリプトは静止)` → `異常発生!` → `システムがスクリプトを叩き起こす`
- 利点:
- 異常がない間、スクリプトはメモリに存在するだけで、CPU負荷はほぼゼロ。
- 変化した瞬間に通知が来るため、リアルタイム性が極めて高い。
今回のテーマである `__InstanceModificationEvent` を使う手法は、まさにこの「イベント駆動」を実現するためのものです。
2. WMIイベント監視のアーキテクチャ
WMIには、特定のオブジェクトの状態が変化したことを検知する「イベント・サブスクリプション」という強力な仕組みがあります。
今回使用する鍵となるクラスは以下の通りです:
- `__InstanceModificationEvent`: インスタンス(プロセスの状態など)が変更された時に発行されるイベント。
- `Win32_PerfFormattedData_PerfProc_Process`: プロセスのパフォーマンスデータ(CPU使用率など)を保持するクラス。
これらを WQL (WMI Query Language) というSQLに似た言語で結合し、「特定のプロセスが、CPU使用率◯%を超えた瞬間にイベントを投げろ」という命令をWindows OSに直接予約するのです。
3. 実装:プロフェッショナル・監視スクリプト
それでは、実際に動作するコードを見ていきましょう。このコードは、特定のプロセス(例:`chrome.exe`)のCPU使用率が閾値を超えた瞬間に、警告メッセージを表示するものです。
' ==========================================================================
' Project: WMI Event-Driven CPU Monitor
' Description: 特定プロセスのCPU使用率異常を、低負荷かつリアルタイムに検知する
' Author: Chief Architect
' ==========================================================================
Option Explicit
' --- 設定セクション ---
Const TARGET_PROCESS = "chrome" ' 監視対象のプロセス名 (拡張子なし)
Const CPU_THRESHOLD = 80 ' 警告を出すCPU使用率の閾値 (%)
Const WATCH_INTERVAL = 1 ' WMIが状態をチェックする間隔 (秒)
' ----------------------
Dim objWMIService, objEvents, objLatestEvent
Dim strQuery, strProcessName, strClassName
' WMIの接続準備
' impersonationLevel=impersonate は、実行ユーザーの権限でWMI操作を行うための作法です
Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
' WQLクエリの構築
' __InstanceModificationEvent WITHIN [間隔] を使うことで、
' OSレベルで「指定秒数ごとに変化を監視し、条件に合致したら通知せよ」と命じます。
strClassName = "Win32_PerfFormattedData_PerfProc_Process"
strProcessName = TARGET_PROCESS
strQuery = "SELECT * FROM __InstanceModificationEvent WITHIN " & WATCH_INTERVAL & _
" WHERE TargetInstance ISA '" & strClassName & "' " & _
" AND TargetInstance.Name = '" & strProcessName & "' " & _
" AND TargetInstance.PercentProcessorTime > " & CPU_THRESHOLD
WScript.Echo "--- 監視を開始しました ---"
WScript.Echo "対象プロセス: " & strProcessName
WScript.Echo "閾値: " & CPU_THRESHOLD & "% 以上"
WScript.Echo "(終了するには Ctrl+C を押してください)"
' 通知クエリの実行
' ExecNotificationQuery は、イベントが発生するまで「待機(ブロック)」する性質を持ちます
Set objEvents = objWMIService.ExecNotificationQuery("WQL", strQuery)
' --- メインイベントループ ---
' このループは、イベントが来るまでスクリプトを「静止状態」に保ちます
' つまり、CPUをほとんど消費しません。
On Error Resume Next ' 実行時エラーによる突然の停止を防止
Do
' 次のイベントが発生するまでここで「ブロック(待機)」する
' これがポーリングとの決定的な違いです!
Set objLatestEvent = objEvents.NextEvent()
If Err.Number = 0 Then
' イベントが発生した場合、TargetInstanceの中に変化したデータが入っている
Dim cpuUsage, procName
procName = objLatestEvent.TargetInstance.Name
cpuUsage = objLatestEvent.TargetInstance.PercentProcessorTime
' 警告の出力(実運用ではログ出力やメール送信、プロセス停止などに置き換え)
WScript.Echo "[!] ALERT: " & Now & " | Process: " & procName & " | CPU: " & cpuUsage & "%"
' ※注意: ここで重い処理を行うと、次のイベント検知が遅れるため、
' 警告処理は極力軽量にするのがプロの鉄則です。
Else
WScript.Echo "エラーが発生しました: " & Err.Description
Exit Do
End If
Loop
On Error GoTo 0
' 後片付け
Set objEvents = Nothing
Set objWMIService = Nothing
4. コードの深層解説:なぜこれが「強い」のか
このコードを単なる「動くもの」として終わらせてはいけません。エンジニアとして、以下の3点に注目してください。
① `ExecNotificationQuery` の魔法
通常の `ExecQuery` は「今の状態」を返して終了しますが、`ExecNotificationQuery` は「未来のイベント」を待ち受ける窓口を作ります。
`objEvents.NextEvent()` を呼び出した瞬間、スクリプトはOSに対して「イベントが来るまで、私を起こさないでください」と伝えます。これにより、Windowsのスケジューラは、このスクリプトにCPU時間を割り当てる必要がなくなるのです。
② WQLの `WITHIN` 句
`WITHIN 1` という記述に注目してください。これは「1秒ごとにWMI内部で状態をチェックせよ」という意味です。
「えっ、結局1秒ごとにチェックしてるならポーリングと同じじゃないか?」と思ったあなた、鋭い!
しかし、「チェックの主体がVBScript(ユーザー空間)か、WMIサービス(カーネルに近いシステム空間)か」という点が決定的に違います。システム空間で行われるチェックは、コンテキストスイッチ(CPUの切り替えコスト)が極めて少なく、圧倒的に低負荷なのです。
③ `TargetInstance` による情報の引き出し
イベントが発生した際、単に「何かが起きた」という通知だけではなく、`TargetInstance` というプロパティを通じて、「変化した後の最新データ」がセットで送られてきます。これにより、わざわざ再度クエリを投げ直す手間(二度手間)を省いています。
5. 陥りやすい罠(トラブルシューティング)
この高度な手法を使う際、初心者が必ずと言っていいほど躓くポイントが3つあります。
1. プロセス名の不一致:
WMIから見えるプロセス名は、タスクマネージャーの表示と微妙に異なることがあります(例:`chrome.exe` ではなく `chrome`)。`TargetInstance.Name` を一度ログに出力して確認する癖をつけましょう。
2. 権限不足:
WMIの高度なクエリ、特にパフォーマンスデータの取得には、管理者権限が必要です。コマンドプロンプトを「管理者として実行」してからスクリプトを叩いてください。
3. 無限ループとエラーハンドリング:
`NextEvent` はイベントが来るまで止まりますが、何らかの理由(WMIサービスの停止など)でエラーが起きると、スクリプトがクラッシュします。`On Error Resume Next` を適切に使い、エラーを捕捉して安全に終了させる設計が、プロフェッショナルの作法です。
最後に:ステップアップを目指すあなたへ
いかがでしたか?
「ループさせて待つ」という直感的な方法から、「イベントを待つ」というシステム的な設計への転換。これこそが、自動化エンジニアが「使い手」から「設計者」へと進化する境界線です。
VBScriptは古い言語だと言われることもありますが、WMIというWindowsの心臓部を直接操る術を知っていれば、今なおこれほど強力な武器はありません。
ここをクリアすれば、VBScriptの基礎はバッチリですよ。次は、この検知をトリガーに「自動でプロセスを再起動する」あるいは「管理者にメールを飛ばす」といった、より実戦的なワークフローに挑戦してみてください。
応援していますよ。次は、もっと複雑なシステムの自動化でお会いしましょう。
