【テクニカル・上級編】【ネットワーク診断】WMI (Win32_PingStatus) を活用した複数拠点への並列 Ping 確認とパケット損失率の自動集計 – VBScript (Visual Basic Scripting Edition)解析バイブル

スポンサーリンク
スポンサーリンク

構造化された静寂:WMI (Win32_PingStatus) によるネットワーク診断の極限実装

現代のエンジニアは、PythonやGoといったモダンな言語で容易にネットワーク診断を実装できる。しかし、Windowsという巨大なエコシステムの深淵において、外部依存を一切排除し、OS標準のCOMインターフェースのみで完結させる「VBScriptによる自動化」には、今なお代替不可能な価値がある。

多くの凡庸なエンジニアは、`WshShell.Run “ping -n 1 …”` と記述し、得られたテキストを正規表現で泥臭くパースしようとする。それは設計上の敗北である。 プロセスの生成コスト、標準出力のバッファリング、そしてロケールに依存する文字列解析。これら全てのオーバーヘッドを抱えながら、不安定な結果を待つのは時間の浪費に他ならない。

真のアーキテクトが選ぶのは、WMI (Windows Management Instrumentation) の `Win32_PingStatus` クラスを直接操作するアプローチだ。

1. なぜ `ping.exe` ではなく WMI なのか

`ping.exe` は人間が読むためのツールであり、プログラムが扱うためのAPIではない。対して WMI は、Windows OS の管理情報を構造化して提供する COM インターフェースである。

`Win32_PingStatus` を利用することで、以下のメリットを享受できる。

  • 構造化データの直接取得: `StatusCode` (0であれば成功) や `ResponseTime` をプロパティとして直接取得でき、文字列パースが不要。
  • プロセス生成コストの排除: 外部プロセスを fork/exec することなく、`wbemcons.dll` を介してカーネルレベルの ICMP リクエストを制御できる。
  • 型安全な制御: 戻り値が明確なため、エラーハンドリングの精度が飛躍的に向上する。

2. オブジェクトライフサイクルとメモリ最適化の鉄則

VBScript (WSH) 環境において、最も警戒すべきはメモリリークとCOMオブジェクトの残留である。特に WMI のような重量級オブジェクトをループ内で扱う場合、明示的な解放 (`Set obj = Nothing`) を怠れば、`wscript.exe` のメモリ消費量は右肩上がりに増大し、最終的にシステムリソースを圧迫する。

また、`GetObject(“winmgmts:…”)` による WMI サービスへの接続は非常にコストが高い。ループの中で接続を繰り返すのは、関数を呼び出すたびにデータベースを再接続するような愚行である。接続オブジェクトは一度だけ生成し、使い回す。 これが鉄則だ。

3. 極限の実装:複数拠点並列Ping診断ツール

以下に、実務レベルで耐えうる堅牢な実装を示す。このコードは、単なる疎通確認ではなく、パケット損失率の集計と構造化レポートの生成を目的としている。

' ==============================================================================
' Network Diagnosis Architect Tool
' Description: WMI (Win32_PingStatus) を利用した高効率ネットワーク診断
' Author: Legendary Chief Architect
' ==============================================================================
Option Explicit

' --- 設定領域 ---
Dim targetHosts, pingCount
targetHosts = Array("8.8.8.8", "1.1.1.1", "192.168.1.1", "invalid.host.local")
pingCount = 4 ' 各ホストへの試行回数

' --- メイン処理 ---
Call ExecuteNetworkDiagnosis()

Sub ExecuteNetworkDiagnosis()
    Dim objWMIService, colItems, objPing, host, i
    Dim successCount, totalCount, resultReport
    
    On Error Resume Next
    ' WMIサービスへの接続 (接続コストが高いため、ループ外で一度だけ実行)
    ' CurrentComputer という名前空間でローカルマシンに接続
    Set objWMIService = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
    
    If Err.Number <> 0 Then
        WScript.Echo "Critical Error: WMIサービスに接続できませんでした。"
        WScript.Quit 1
    End If
    On Error GoTo 0

    resultReport = "Host,SuccessRate,AvgResponseTime(ms),Status" & vbCrLf

    ' ホストリストに対する反復処理
    For Each host In targetHosts
        successCount = 0
        totalCount = 0
        Dim sumResponseTime
        sumResponseTime = 0

        For i = 1 To pingCount
            ' Win32_PingStatus のインスタンスを生成
            ' Getメソッドを使用して、特定のホストに対するPingステータスを取得
            Set objPing = objWMIService.Get("Win32_PingStatus.Address='" & host & "'")
            
            ' メソッドの実行 (この時点で実際に ICMP パケットが送信される)
            ' 注意: Getメソッドで取得したオブジェクトのプロパティを読み取ることでトリガーされる
            If Not objPing Is Nothing Then
                ' StatusCode 0 = Success
                If objPing.StatusCode = 0 Then
                    successCount = successCount + 1
                    sumResponseTime = sumResponseTime + objPing.ResponseTime
                End If
                totalCount = totalCount + 1
                
                ' オブジェクトの即時解放 (メモリリーク防止の最重要ポイント)
                Set objPing = Nothing
            End If
        Next

        ' 統計計算
        Dim lossRate, avgTime, finalStatus
        lossRate = (successCount / totalCount) * 100
        If successCount > 0 Then
            avgTime = sumResponseTime / successCount
        Else
            avgTime = 0
        End If
        
        finalStatus = IIf(successCount = pingCount, "Healthy", IIf(successCount > 0, "Degraded", "Down"))
        
        resultReport = resultReport & host & "," & lossRate & "%," & avgTime & "ms," & finalStatus & vbCrLf
    Next

    ' 結果の出力 (実際にはファイル書き出しやAPI送信を想定)
    WScript.Echo "--- Network Diagnosis Report ---" & vbCrLf & resultReport

    ' WMI接続オブジェクトの明示的解放
    Set objWMIService = Nothing
End Sub

' 三項演算子の代替関数 (VBScriptには存在しないため実装)
Function IIf(condition, trueVal, falseVal)
    If condition Then
        IIf = trueVal
    Else
        IIf = falseVal
    End If
End Function

4. アーキテクトによる深掘り解説

4.1 `Win32_PingStatus` の内部挙動

このクラスは、内部的に `IcmpSendEcho` API をラップしている。特筆すべきは、`objWMIService.Get` を呼び出した時点ではクエリの準備が行われるだけであり、プロパティ(`StatusCode` など)にアクセスした瞬間に同期的なネットワーク I/O が発生する点だ。

4.2 「並列処理」の擬似的な実現について

VBScript はシングルスレッド言語である。したがって、上記のコードは厳密には「逐次処理」である。しかし、数百台のホストを診断する必要がある場合、以下の戦略を採る。

1. プロセスの分断: ホストリストを N 分割し、`WshShell.Run “cscript.exe diagnosis.vbs group1.txt”` のように複数の `cscript.exe` インスタンスを非同期で起動する。
2. COMの非同期呼び出し: 本来、COMインターフェースには非同期メソッドが存在するが、VBScript の言語仕様レベルではこれを制御するのが困難である。そのため、OSレベルでのプロセス並列化が最も確実かつ高速である。

4.3 パフォーマンス上のボトルネック

WMI のクエリは、内部的に WMI リポジトリへのアクセスを伴うため、純粋な Win32 API 呼び出しに比べれば低速だ。しかし、`ping.exe` を数千回起動することに比べれば、プロセス生成のオーバーヘッドがない分、圧倒的に効率的である。

5. 結論

レガシーな環境での保守とは、単に古いコードを維持することではない。その環境で利用可能な最高の技術スタックを組み合わせ、現代的な設計思想(構造化データ、メモリ管理、計算量意識)を適用することである。

`Win32_PingStatus` を掌握することは、Windows の管理基盤である WMI の本質を理解することに繋がる。外部コマンドという「ブラックボックス」に頼らず、OS のインターフェースを直接制御せよ。それが、真のシステムエンジニアとしての矜持である。

タイトルとURLをコピーしました