【ICMP応答時間自動計測】WMI (Win32_PingStatus) を活用した複数拠点ネットワークのレイテンシ監視スクリプト
レガシーシステムの維持、あるいは閉域網におけるエージェントレスな監視において、VBScript(Visual Basic Scripting Edition)とWSH(Windows Script Host)のコンビネーションは依然として強力なカードである。余計なランタイムのインストールを必要とせず、Windowsが標準搭載するインフラストラクチャだけで完結する機動性は、他の言語の追随を許さない。
ネットワーク監視の現場において、外部の `ping.exe` プロセスを起動し、その標準出力を正規表現や文字列操作でパース(解析)する手法は、文字コードの差異(UTF-8、Shift-JIS、OEMコードページ)やOSの言語ロケール変更によっていとも容易く破綻する。脆弱でメンテナンス性の低いバッチ処理の系譜から脱却し、OSの管理基盤である WMI(Windows Management Instrumentation) を直接叩くことで、レイテンシやパケットロス率を「構造化データ」として安全に回収する極限のアーキテクチャをここに提示する。
—
1. なぜ `ping.exe` のパース地獄から脱却すべきなのか
`ping` コマンドの結果をテキストベースで処理するアプローチには、以下の致命的な構造的欠陥がある。
- ロケール依存性: 英語環境では `Reply from…`、日本語環境では `返信: …` となり、OSの言語パックに依存してパースロジックが崩壊する。
- プロセス生成のオーバーヘッド: 外部プロセスを生成する度に `cmd.exe` および `ping.exe` のインスタンスが立ち上がり、メモリとCPUリソースを無駄に消費する(特に複数拠点をループ監視する場合に顕著)。
- エラーハンドリングの欠如: ネットワーク断や名前解決失敗時の終了コード(Exit Code)の解釈が曖昧になりがちである。
WMIの `Win32_PingStatus` クラスは、これらの課題を根底から解決する。COMコンポーネント経由でOSのネットワークスタックに直接クエリを投げ、結果をオブジェクトとして取得するため、言語依存のない安定した数値・ステータスハンドリングが可能となる。
—
2. アーキテクチャの要点:WMIクエリとメモリ管理の鉄則
VBScriptによるWMI操作において、シニアエンジニアが最も厳格に管理しなければならないのが COMオブジェクトのライフサイクル である。
`GetObject(“winmgmts:”)` や `CreateObject` で生成された参照は、スクリプトのスコープが終了するまでメモリ上に残存し、特に長期間稼働する常駐型スクリプトや、多重ループを回す監視スクリプトではメモリリーク(COM参照リーク)の温床となる。
明示的な `Nothing` 代入による即座の解放、そしてエラー発生時でも確実にリソースを解放する構文(VBScriptにおける厳格なエラー制御)が不可欠である。
—
3. 実装コード:複数拠点ネットワーク監視スクリプト
以下のスクリプトは、指定した複数拠点のIPアドレスに対して順次ICMPリクエストを送信し、応答時間(レイテンシ)とステータスを構造化してCSVログに出力するプロダクション品質のコードである。
‘ ==============================================================================
‘ Script Name : NetworkLatencyMonitor.vbs
‘ Description : WMI (Win32_PingStatus) を用いた複数拠点ネットワーク監視
‘ Author :Chief Architect
‘ ==============================================================================
Option Explicit
‘ エラー時の動作を制御(On Error Resume Nextを使用する場合は必ず適切に復帰させること)
On Error Resume Next
Dim objFSO, objFile, strLogPath
Dim arrTargets, strTarget
Dim objWMIService, colPingStatus, objStatus
Dim strWMIQuery
Dim intTimeout, intBufferSize
Dim dtsLogTime
‘ — [設定セクション] —
intTimeout = 1000 ‘ タイムアウト (ミリ秒)
intBufferSize = 32 ‘ 送信パケットサイズ (バイト)
strLogPath = “C:\Logs\NetworkLatency_” & FormatDate(Now) & “.csv”
‘ 監視対象の拠点IPアドレスまたはホスト名リスト
arrTargets = Array(“192.168.1.1”, “192.168.10.254”, “10.0.50.1”, “dc01.corp.local”)
‘ — [ファイルシステム初期化] —
Set objFSO = CreateObject(“Scripting.FileSystemObject”)
‘ ログファイルの存在確認とヘッダー出力
If Not objFSO.FileExists(strLogPath) Then
Set objFile = objFSO.OpenTextFile(strLogPath, 8, True, 0) ‘ 8: ForAppending, 0: ASCII
objFile.WriteLine “Timestamp,Target,StatusCode,ResponseTime_ms,ProtocolAddress,Bytes”
objFile.Close
Set objFile = Nothing
End If
‘ — [WMI接続の確立 (ローカル)] —
‘ ※リモートマシンを監視する場合は “winmgmts:{impersonationLevel=impersonate}!\\RemoteComputer\root\cimv2” を指定
Set objWMIService = GetObject(“winmgmts:\\.\root\cimv2”)
If Err.Number <> 0 Then
WScript.Echo “CRITICAL: WMIサービスへの接続に失敗しました。Error: ” & Hex(Err.Number)
WScript.Quit 1
End If
‘ — [メインループ:複数拠点へのポーリング] —
For Each strTarget In arrTargets
dtsLogTime = Now()
‘ WQLクエリの構築 (Win32_PingStatusはインスタンス生成ではなくクエリ経由でパラメータを渡す特殊なクラス)
strWMIQuery = “SELECT StatusCode, ResponseTime, ProtocolAddress, BufferSize FROM Win32_PingStatus WHERE Address = ‘” & strTarget & “‘ AND Timeout = ” & intTimeout & ” AND BufferSize = ” & intBufferSize
Set colPingStatus = objWMIService.ExecQuery(strWMIQuery)
If Err.Number <> 0 Then
‘ クエリ実行時エラーのハンドリング
LogResult dtsLogTime, strTarget, -999, -1, “Query Error”, 0
Err.Clear
Else
For Each objStatus in colPingStatus
If IsNull(objStatus.StatusCode) Or objStatus.StatusCode <> 0 Then
‘ タイムアウトまたは到達不能
Dim statusCodeVal
If IsNull(objStatus.StatusCode) Then
statusCodeVal = -1 ‘ タイムアウト等の理由でプロパティがNullの場合
Else
statusCodeVal = objStatus.StatusCode
End If
LogResult dtsLogTime, strTarget, statusCodeVal, -1, strTarget, 0
Else
‘ 正常応答
LogResult dtsLogTime, strTarget, objStatus.StatusCode, objStatus.ResponseTime, objStatus.ProtocolAddress, objStatus.BufferSize
End If
Next
End If
‘ ループ内でのCOMオブジェクトの確実な解放
Set colPingStatus = Nothing
Next
‘ — [クリーンアップ] —
Set objWMIService = Nothing
Set objFSO = Nothing
WScript.Echo “INFO: ネットワーク監視ポーリングが正常終了しました。”
WScript.Quit 0
‘ ==============================================================================
‘ サブルーチン: ログ出力処理
‘ ==============================================================================
Sub LogResult(pTime, pTarget, pStatus, pTimeMs, pAddress, pBytes)
Dim fsoLog, tsLog
Set fsoLog = CreateObject(“Scripting.FileSystemObject”)
Set tsLog = fsoLog.OpenTextFile(strLogPath, 8, True, 0)
tsLog.WriteLine FormatDateTime(pTime, 2) & ” ” & FormatDateTime(pTime, 3) & “,” & _
pTarget & “,” & _
pStatus & “,” & _
pTimeMs & “,” & _
pAddress & “,” & _
pBytes
tsLog.Close
Set tsLog = Nothing
Set fsoLog = Nothing
End Sub
‘ ==============================================================================
‘ 補助関数: ログファイル名用の日付文字列生成 (YYYYMMDD)
‘ ==============================================================================
Function FormatDate(pDate)
Dim yyyy, mm, dd
yyyy = CStr(Year(pDate))
mm = Right(“0” & CStr(Month(pDate)), 2)
dd = Right(“0” & CStr(Day(pDate)), 2)
FormatDate = yyyy & mm & dd
End Function
—
4. コードの深層解説:シニアエンジニアが押さえるべきポイント
1. `Win32_PingStatus` の特殊なインスタンス生成メカニズム
通常のWMIクラス(例: `Win32_Process` や `Win32_Service`)は `InstancesOf` で全取得できるが、`Win32_PingStatus` は抽象クラスまたは動的クエリ専用クラスに近い挙動をする。そのため、通常のインスタンス作成ではなく、`WHERE` 句にパラメータ(`Address`, `Timeout`, `BufferSize`)を直接埋め込んだWQLを発行することで、その場でOSにパケットを飛ばさせて結果をバインドする仕組みになっている。
2. `Null` 安全性の担保
ネットワーク機器が完全に沈黙している場合や名前解決に失敗した場合、WMIが返す `StatusCode` や `ResponseTime` は単なる `0` ではなく `Null` となる。VBScriptでこれをそのまま数値演算や文字列結合に使うと型ミスマッチ(エラー 13)を引き起こす。`IsNull()` 関数による厳格な事前判定が、レガシー環境におけるクラッシュを防ぐ防壁となる。
3. ASCIIコード指定によるファイルI/O
`OpenTextFile` の第4引数に `0`(TristateFalse = ASCII)を指定している点に注目せよ。デフォルト(TristateUseDefault = -2)に依存すると、実行するWindows ServerのロケールやグループポリシーによってANSI(Shift-JIS)かUTF-8かが揺らぎ、後続のExcelやBIツールでのインポート時に文字化けの惨劇を引き起こす。インフラ監視のログは常にASCII/Shift-JISの堅牢な基盤で出力すべきである。
—
5. 運用への組み込みとさらなる高みへ
このVBScriptをタスクスケジューラに登録し、例えば「5分おき」に実行させることで、追加のサードパーティ製監視ツールを入れる権限がない厳格なセキュリティ環境であっても、完全なレイテンシ・死活監視基盤を構築できる。
さらに高度なシステム間連携を目指すのであれば、取得したCSVデータをそのままPowerShellや別プロセスのAPI経由でSlack/TeamsのWebhooksに飛ばしたり、データベースへバルクインサートする前段の「超軽量プローブ」として、このVBScriptアーキテクチャを活用してほしい。レガシーとモダンを繋ぐ知見こそが、システムを真に堅牢にする。
