VBAからWMI/CIMでシステム情報取得

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

VBAからWMI/CIMでシステム情報取得

背景と要件

企業内のPC資産管理やトラブルシューティングにおいて、各クライアントPCのシステム情報を迅速かつ正確に把握することは重要です。手動での情報収集は非効率であり、誤入力のリスクも伴います。VBA(Visual Basic for Applications)はMicrosoft Office製品に組み込まれた強力な自動化ツールであり、Windowsに標準搭載されているWMI (Windows Management Instrumentation) およびその基盤であるCIM (Common Information Model) を利用することで、外部ライブラリに依存せず、PCのハードウェアやOSに関する詳細な情報を取得できます。 、VBAからWMI/CIMを介してシステム情報を取得する具体的な方法を解説します。特に、ExcelとAccessの二つの異なるOfficeアプリケーションを対象に、実務レベルで再現可能なコードを提供します。さらに、取得処理の性能を最大化するためのチューニング手法、運用上の考慮点、および潜在的な落とし穴についても掘り下げます。外部ライブラリは一切使用せず、必要に応じてWin32 APIをDeclare PtrSafeで宣言して使用しますが、WMIアクセス自体はCOMインターフェースを介するため、主にGetObject関数を使用します。

設計

VBAからWMIを利用したシステム情報取得の基本的な流れは以下の通りです。

  1. WMIサービスへの接続: GetObject("winmgmts:\\.\root\cimv2")関数を使用して、ローカルPCのWMIサービスオブジェクトを取得します。これにより、WMIの様々な管理機能にアクセスするための起点となります。

  2. WQLクエリの実行: WQL (WMI Query Language) はSQLに似たクエリ言語で、WMIオブジェクトをフィルタリング、取得するために使用します。取得したWMIサービスオブジェクトのExecQueryメソッドにWQLクエリ文字列を渡します。

  3. 結果の反復処理とデータ抽出: ExecQueryメソッドは、クエリ結果としてSWbemObjectSetコレクションを返します。このコレクション内の各SWbemObjectが、特定のWMIクラスのインスタンス(例: 1つのCPU、1つの論理ディスク)に対応します。各オブジェクトのプロパティを読み出し、必要な情報を抽出します。

  4. データ格納: 抽出した情報をExcelシート、Accessテーブル、またはVBAの配列に格納します。

  5. リソース解放: 処理が完了したら、取得したWMIオブジェクトを適切に解放します。

この一連の流れを図にすると次のようになります。

graph TD
    A["開始"] --> B{"WMIサービスへの接続"};
    B -- GetObject("winmgmts:\\.\root\cimv2")でWMIサービスオブジェクト取得 --> C["SWbemServicesオブジェクト取得"];
    C --> D{"WQLクエリの実行"};
    D -- ExecQuery("SELECT * FROM Win32_...")でSWbemObjectSet取得 --> E["SWbemObjectSet取得"];
    E --> F{"各SWbemObjectの反復処理"};
    F -- プロパティ抽出とデータ整形 --> G["データ格納 (Excelシート/Accessテーブル/配列)"];
    G --> H{"全てのオブジェクトを処理"};
    H -- 処理完了 --> I["WMIオブジェクトのリソース解放"];
    I --> J["終了"];

実装

ここでは、ExcelとAccessでの具体的な実装例を示します。

1. Excelでのシステム情報取得と性能チューニング

このVBAコードは、現在のPCのOS情報、CPU情報、メモリ情報をExcelシートに整理して出力します。特に、大量の情報を扱う場合に有効な性能チューニング手法を組み込んでいます。

Option Explicit

'---------------------------------------------------------------------------------------------------
' プロシージャ名: GetSystemInfoToExcel
' 概要: WMI/CIMを使用してWindowsのシステム情報を取得し、Excelシートに出力します。
'       OS情報、CPU情報、メモリ情報などを収集し、整形して表示します。
' 入力: なし
' 出力: アクティブシートにシステム情報が書き込まれます。
' 前提:
'   - Excelがインストールされていること。
'   - VBAエディタで「Microsoft WMI Scripting Library」への参照設定は不要です。
'     GetObject関数で動的にWMIサービスに接続します。
'   - 実行環境のセキュリティ設定でWMIへのアクセスが許可されていること。
' 計算量: WMIクエリの実行回数に比例。各クエリはシステムの状態により変動しますが、
'         一般的に数秒以内で完了します。
' メモリ条件: 取得する情報量に依存しますが、通常はMB単位で十分です。
'---------------------------------------------------------------------------------------------------
Sub GetSystemInfoToExcel()
    Dim objWMIService As Object
    Dim colItems As Object
    Dim objItem As Object
    Dim ws As Worksheet
    Dim lngRow As Long
    Dim strQuery As String
    Dim varData(1 To 100, 1 To 2) As Variant ' データバッファ用配列 (仮の最大サイズ)
    Dim i As Long
    Dim startTime As Double

    ' 性能チューニングの開始
    startTime = Timer ' 処理開始時刻を記録
    Set ws = ThisWorkbook.Sheets(1)
    ws.Cells.ClearContents ' 既存のデータをクリア

    With Application
        .ScreenUpdating = False         ' 画面描画を停止
        .Calculation = xlCalculationManual ' 計算モードを手動に
        .EnableEvents = False           ' イベントを停止
    End With

    On Error GoTo ErrorHandler

    ' WMIサービスに接続
    Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")

    ' ヘッダーの書き出し (配列に格納してから一括書き出し)
    i = i + 1: varData(i, 1) = "項目名": varData(i, 2) = "値"
    i = i + 1: varData(i, 1) = "取得日時": varData(i, 2) = Format(Now, "yyyy/mm/dd hh:nn:ss")
    i = i + 1: varData(i, 1) = "" ' 空行

    ' --- 1. OS情報の取得 ---
    strQuery = "SELECT Caption, Version, OSArchitecture, SerialNumber, TotalVisibleMemorySize, FreePhysicalMemory FROM Win32_OperatingSystem"
    Set colItems = objWMIService.ExecQuery(strQuery)
    For Each objItem In colItems
        i = i + 1: varData(i, 1) = "OS情報": varData(i, 2) = ""
        i = i + 1: varData(i, 1) = "  OS名": varData(i, 2) = objItem.Caption
        i = i + 1: varData(i, 1) = "  バージョン": varData(i, 2) = objItem.Version
        i = i + 1: varData(i, 1) = "  アーキテクチャ": varData(i, 2) = objItem.OSArchitecture
        i = i + 1: varData(i, 1) = "  シリアル番号": varData(i, 2) = objItem.SerialNumber
        i = i + 1: varData(i, 1) = "  物理メモリ総量(GB)": varData(i, 2) = Format(objItem.TotalVisibleMemorySize / 1024 / 1024, "0.00")
        i = i + 1: varData(i, 1) = "  空き物理メモリ(GB)": varData(i, 2) = Format(objItem.FreePhysicalMemory / 1024 / 1024, "0.00")
        Exit For ' OSは通常1つなので最初の項目のみ取得
    Next
    i = i + 1: varData(i, 1) = ""

    ' --- 2. CPU情報の取得 ---
    strQuery = "SELECT Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed FROM Win32_Processor"
    Set colItems = objWMIService.ExecQuery(strQuery)
    For Each objItem In colItems
        i = i + 1: varData(i, 1) = "CPU情報": varData(i, 2) = ""
        i = i + 1: varData(i, 1) = "  プロセッサ名": varData(i, 2) = objItem.Name
        i = i + 1: varData(i, 1) = "  コア数": varData(i, 2) = objItem.NumberOfCores
        i = i + 1: varData(i, 1) = "  論理プロセッサ数": varData(i, 2) = objItem.NumberOfLogicalProcessors
        i = i + 1: varData(i, 1) = "  最大クロック周波数(MHz)": varData(i, 2) = objItem.MaxClockSpeed
        Exit For ' 通常1つのCPU情報を代表として取得
    Next
    i = i + 1: varData(i, 1) = ""

    ' --- 3. コンピュータシステム情報の取得 ---
    strQuery = "SELECT Name, Manufacturer, Model FROM Win32_ComputerSystem"
    Set colItems = objWMIService.ExecQuery(strQuery)
    For Each objItem In colItems
        i = i + 1: varData(i, 1) = "PC情報": varData(i, 2) = ""
        i = i + 1: varData(i, 1) = "  コンピュータ名": varData(i, 2) = objItem.Name
        i = i + 1: varData(i, 1) = "  メーカー": varData(i, 2) = objItem.Manufacturer
        i = i + 1: varData(i, 1) = "  モデル": varData(i, 2) = objItem.Model
        Exit For
    Next
    i = i + 1: varData(i, 1) = ""

    ' 配列の内容を一括でシートに書き出し
    ws.Range("A1").Resize(i, 2).Value = varData
    ws.Columns("A:B").AutoFit ' 列幅を自動調整

Finalize:
    ' 性能チューニングの終了
    With Application
        .ScreenUpdating = True
        .Calculation = xlCalculationAutomatic
        .EnableEvents = True
    End With

    ' オブジェクトの解放
    Set objItem = Nothing
    Set colItems = Nothing
    Set objWMIService = Nothing
    Set ws = Nothing

    Debug.Print "処理時間: " & Format(Timer - startTime, "0.00") & "秒"
    MsgBox "システム情報の取得が完了しました。", vbInformation

    Exit Sub

ErrorHandler:
    With Application
        .ScreenUpdating = True
        .Calculation = xlCalculationAutomatic
        .EnableEvents = True
    End With
    MsgBox "エラーが発生しました: " & Err.Description & vbCrLf & _
           "WMIサービスへのアクセス権限、またはクエリの内容を確認してください。", vbCritical
    Resume Finalize
End Sub

2. Accessでの詳細システム情報取得と出力

このVBAコードは、現在のPCの論理ディスク情報とネットワークアダプター情報をAccessのイミディエイトウィンドウに出力します。Accessのフォームやレポートに直接表示することも可能ですが、ここでは簡潔にイミディエイトウィンドウに出力します。

Option Compare Database
Option Explicit

'---------------------------------------------------------------------------------------------------
' プロシージャ名: GetDetailedSystemInfoForAccess
' 概要: WMI/CIMを使用してWindowsの論理ディスクとネットワークアダプター情報を取得し、
'       Accessのイミディエイトウィンドウに出力します。
' 入力: なし
' 出力: イミディエイトウィンドウに詳細情報が出力されます。
' 前提:
'   - Accessがインストールされていること。
'   - VBAエディタで「Microsoft WMI Scripting Library」への参照設定は不要です。
'     GetObject関数で動的にWMIサービスに接続します。
'   - 実行環境のセキュリティ設定でWMIへのアクセスが許可されていること。
' 計算量: WMIクエリの実行回数に比例。各クエリはシステムの状態により変動しますが、
'         一般的に数秒以内で完了します。
' メモリ条件: 取得する情報量に依存しますが、通常はMB単位で十分です。
'---------------------------------------------------------------------------------------------------
Sub GetDetailedSystemInfoForAccess()
    Dim objWMIService As Object
    Dim colItems As Object
    Dim objItem As Object
    Dim strQuery As String
    Dim startTime As Double

    startTime = Timer ' 処理開始時刻を記録

    On Error GoTo ErrorHandler

    ' WMIサービスに接続
    Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")

    Debug.Print "--- システム詳細情報 (取得日時: " & Format(Now, "yyyy/mm/dd hh:nn:ss") & ") ---"
    Debug.Print ""

    ' --- 1. 論理ディスク情報の取得 ---
    Debug.Print "### 論理ディスク情報 ###"
    strQuery = "SELECT DeviceID, VolumeName, Size, FreeSpace, FileSystem FROM Win32_LogicalDisk WHERE DriveType = 3" ' DriveType=3はローカルディスク
    Set colItems = objWMIService.ExecQuery(strQuery)
    If colItems.Count > 0 Then
        For Each objItem In colItems
            Debug.Print "  デバイスID: " & objItem.DeviceID
            Debug.Print "    ボリューム名: " & IIf(IsNull(objItem.VolumeName), "(なし)", objItem.VolumeName)
            Debug.Print "    ファイルシステム: " & objItem.FileSystem
            Debug.Print "    総容量: " & Format(objItem.Size / 1024 / 1024 / 1024, "0.00") & " GB"
            Debug.Print "    空き容量: " & Format(objItem.FreeSpace / 1024 / 1024 / 1024, "0.00") & " GB"
            Debug.Print "-----------------------------"
        Next
    Else
        Debug.Print "  ローカルディスク情報が見つかりませんでした。"
    End If
    Debug.Print ""

    ' --- 2. ネットワークアダプター情報の取得 ---
    Debug.Print "### ネットワークアダプター情報 ###"
    strQuery = "SELECT Description, IPAddress, MACAddress, ServiceName FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = TRUE"
    Set colItems = objWMIService.ExecQuery(strQuery)
    If colItems.Count > 0 Then
        For Each objItem In colItems
            Debug.Print "  説明: " & objItem.Description
            If Not IsNull(objItem.IPAddress) Then
                Debug.Print "    IPアドレス: " & Join(objItem.IPAddress, ", ")
            End If
            Debug.Print "    MACアドレス: " & objItem.MACAddress
            Debug.Print "    サービス名: " & objItem.ServiceName
            Debug.Print "-----------------------------"
        Next
    Else
        Debug.Print "  IP有効なネットワークアダプター情報が見つかりませんでした。"
    End If
    Debug.Print ""

Finalize:
    ' オブジェクトの解放
    Set objItem = Nothing
    Set colItems = Nothing
    Set objWMIService = Nothing

    Debug.Print "処理時間: " & Format(Timer - startTime, "0.00") & "秒"
    MsgBox "システム詳細情報の取得が完了しました。", vbInformation
    Exit Sub

ErrorHandler:
    MsgBox "エラーが発生しました: " & Err.Description & vbCrLf & _
           "WMIサービスへのアクセス権限、またはクエリの内容を確認してください。", vbCritical
    Resume Finalize
End Sub

性能チューニング

VBAでWMIからシステム情報を取得する際の性能は、WMIクエリ自体の実行速度と、VBAがその結果を処理しOfficeアプリケーションに書き込む速度の両方に依存します。

  1. Excelの描画・計算・イベントの停止:

    • Application.ScreenUpdating = False: 画面の更新を停止することで、セルの書き込み処理が格段に速くなります。多くのセルに書き込む場合、5倍〜10倍、あるいはそれ以上の速度向上が期待できます。処理終了時にTrueに戻すことを忘れないでください。

    • Application.Calculation = xlCalculationManual: シートに多数の計算式がある場合、手動計算モードに設定することで、データ書き込みのたびに発生する自動再計算を抑制し、処理時間を大幅に短縮できます。処理終了時にxlCalculationAutomaticに戻します。

    • Application.EnableEvents = False: マクロがシート変更などのイベントをトリガーしないようにすることで、予期せぬイベントプロシージャの実行を防ぎ、安定性と速度を向上させます。処理終了時にTrueに戻します。

  2. 配列バッファリング (Excel):

    • セルに1つずつデータを書き込むのは非常に効率が悪いです。代わりに、VBAの配列に一度データを格納し、処理の最後にRange.Value = Arrayのように範囲に対して配列を一括で書き込むことで、COMインタラクションのオーバーヘッドを削減します。これにより、数百〜数千行のデータ書き込みで5倍〜20倍の速度向上が期待できます。上記のExcelコードでは、varData配列に格納後、ws.Range("A1").Resize(i, 2).Value = varDataで一括書き込みを行っています。
  3. WMIクエリの最適化:

    • SELECT *ではなく、必要なプロパティのみを明示的に指定する(例: SELECT Caption, Version FROM Win32_OperatingSystem)。これにより、WMIが返すデータ量が減り、ネットワーク経由でのリモートWMIアクセス時に特に有効です。

    • WHERE句を適切に利用して、取得するインスタンス数を最小限に抑える。例えば、特定の種類のディスクのみを対象にするなどです。

  4. オブジェクトの解放:

    • WMIオブジェクトはCOMオブジェクトであるため、不要になったら速やかにSet obj = Nothingで解放することが推奨されます。これにより、メモリリークの防止とリソースの効率的な利用を促進します。

検証

取得したシステム情報が正確であることを確認するには、以下の方法を推奨します。

  1. 手動確認:

    • OS情報: Windowsの設定 (Win + Iシステムバージョン情報) やコマンドプロンプト (winver コマンド) で表示されるOS名、バージョン、ビルド番号などと比較します。シリアル番号はwmic os get serialnumberコマンドで確認できます。

    • CPU情報: タスクマネージャーの「パフォーマンス」タブ(CPU)や、システム情報 (msinfo32 コマンド) でプロセッサ名、コア数、論理プロセッサ数などを確認します。

    • PC情報: システム情報 (msinfo32 コマンド) でシステムモデルやメーカーを確認します。

    • 論理ディスク情報: エクスプローラーでPCのドライブを右クリックし「プロパティ」を開いて容量、空き容量、ファイルシステムを確認します。

    • ネットワークアダプター情報: コマンドプロンプトでipconfig /allを実行し、IPアドレスやMACアドレス、説明などを比較します。

  2. WMI Explorerなどのツール: WMI情報をGUIで閲覧できるサードパーティ製ツール(例: WMI Explorer、WMICodeCreator)を使用して、WMIクラスのインスタンスとそのプロパティを直接確認し、VBAコードで取得した値と一致するかを検証します。

これらの検証を通じて、コードが意図した通りに情報を取得できているか、またWMIクエリが正しい結果を返しているかを確認できます。

運用

実行手順

  1. VBAエディタの起動: 対象のExcelファイルまたはAccessデータベースファイルを開き、Alt + F11キーを押してVBAエディタを起動します。

  2. モジュールの挿入: 「挿入」メニューから「標準モジュール」を選択します。

  3. コードの貼り付け: 新しく開いたモジュールウィンドウに、上記のExcelまたはAccess用のVBAコードを貼り付けます。

  4. マクロの実行:

    • Excel: VBAエディタでGetSystemInfoToExcelプロシージャ内にカーソルを置き、F5キーを押すか、Excelシートから「開発」タブ→「マクロ」を選択し、GetSystemInfoToExcelを選んで「実行」をクリックします。

    • Access: VBAエディタでGetDetailedSystemInfoForAccessプロシージャ内にカーソルを置き、F5キーを押します。イミディエイトウィンドウ (Ctrl + Gで表示) に結果が出力されます。

ロールバック方法

本記事で提供するVBAコードは、システムの情報を読み取るだけで、根本的なシステム設定の変更や重要なファイルの削除を行うものではありません。そのため、複雑なロールバック手順は不要です。

  1. マクロの停止: 実行中のマクロはCtrl + Breakキーで強制停止できます。

  2. 出力データの削除:

    • Excel: マクロによってシートに書き込まれたデータは、シートの内容をクリアするか、該当するシートを削除することで元に戻せます。

    • Access: イミディエイトウィンドウに出力されたデータは、ウィンドウをクリアすれば消えます。もしテーブルに書き込むようにコードを改変していた場合は、該当レコードを削除することでロールバックできます。

  3. VBAコードの削除: VBAエディタで、コードを貼り付けた標準モジュールを右クリックし、「Module1の削除」(またはモジュール名に応じた項目)を選択して、VBAプロジェクトからコードを削除できます。これにより、マクロが存在しない状態に戻ります。

落とし穴と注意点

  1. アクセス権限: WMIはシステム管理情報にアクセスするため、実行ユーザーに適切な権限が必要です。通常、ローカル管理者権限を持つユーザーであれば問題ありませんが、標準ユーザーで実行する場合は一部の情報にアクセスできない可能性があります。

  2. WMIサービスの可用性: WindowsのWMIサービスが停止している、または破損している場合、GetObject関数が失敗し、エラーが発生します。この場合、Windowsの「サービス」管理ツールで「Windows Management Instrumentation」サービスが実行中であるかを確認してください。

  3. WQLクエリの正確性: WQLクエリが正しくない場合、期待するデータが取得できなかったり、エラーが発生したりします。WMIクラス名やプロパティ名は大文字・小文字を区別する場合があるため、正確に記述する必要があります。Microsoft LearnのWMIドキュメントでWMIクラスのリファレンスを参照することが重要です(例: https://learn.microsoft.com/en-us/windows/win32/cimwin32a/win32-classes)。

  4. プロパティのNULL値: WMIオブジェクトのプロパティにはNull値が含まれることがあります。VBAでこれらを処理する際は、IsNull()関数を使って適切にチェックし、エラーを避ける必要があります。上記のAccessコードではIIf(IsNull(objItem.VolumeName), "(なし)", objItem.VolumeName)のように処理しています。

  5. リモートPCへのアクセス: GetObject("winmgmts:\\.\root\cimv2")\\.\部分を\\<ComputerName>\に変更することでリモートPCのWMIにアクセスできますが、ファイアウォール設定、DCOM権限、ユーザー認証など、追加のセキュリティ設定が必要です。本記事ではローカルPCへのアクセスに限定しています。

  6. 性能と遅延: WMIクエリはシステムに負荷をかけることがあり、特にリモートアクセスや複雑なクエリでは時間がかかる場合があります。非同期処理はVBAでは直接サポートされていませんが、長時間かかる処理ではユーザーインターフェースがフリーズしないよう、進行状況をフィードバックするなどの工夫が推奨されます。

まとめ

本記事では、VBAからWMI/CIMを利用してWindowsのシステム情報を取得する包括的な手法を解説しました。GetObject("winmgmts:\\.\root\cimv2")によるWMIサービスへの接続とWQLクエリExecQueryメソッドの使用は、VBAにおけるシステム情報取得の標準的なアプローチです。2024年7月30日 (JST) 時点でも、このCOMインターフェースを介した方法は安定して利用できます。

ExcelとAccessの具体的なコード例を通じて、OS、CPU、メモリ、ディスク、ネットワークアダプターといった多岐にわたるシステム情報を取得する方法を示し、Application.ScreenUpdating = Falseや配列バッファリングなどの性能チューニングが、特にOfficeアプリケーションへのデータ書き込みにおいて劇的な速度向上(例: 5倍〜100倍)をもたらすことを強調しました。

WMI/CIMはWindows環境の強力な管理基盤であり、VBAとの連携により、外部ライブラリに頼ることなく柔軟かつ効率的なシステム情報収集が可能です。本記事で提供された知識とコードは、資産管理、トラブルシューティング、インベントリ収集といった多様な実務シナリオで役立つでしょう。運用上の注意点や潜在的な落とし穴を理解することで、より堅牢で信頼性の高い自動化ソリューションを構築できます。

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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