本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。
VBAとWMIによるWindowsシステム情報の取得と自動化
背景と要件
Officeアプリケーション(Excel, Accessなど)を用いた業務自動化において、実行環境であるWindowsシステムの情報を取得する必要が生じる場面は少なくありません。例えば、資産管理、トラブルシューティング時の情報収集、特定のハードウェア構成に基づく処理の分岐などです。このような要件に対し、VBA単体ではOSやハードウェアの詳細情報にアクセスすることが困難です。 、VBAからWindows Management Instrumentation (WMI) を利用して、Windowsシステムの詳細情報を取得し、Officeアプリケーションで活用する手法を解説します。外部ライブラリに依存せず、Win32 APIの直接利用も最小限に抑え、VBAの標準機能とWMIの連携に焦点を当てます。実務レベルで再現可能なExcel/Access向けコードを提供し、性能チューニングや運用上の注意点も詳述します。
設計
WMIは、Windowsシステムの管理データと操作を一元的に提供するためのMicrosoftの技術です。VBAからWMIを利用するには、GetObject関数でWMIサービスに接続し、WQL (WMI Query Language) を用いて特定のWMIクラスから情報を照会します。
処理フロー
VBAでWMIを利用したシステム情報取得の基本的な流れは以下の通りです。
graph TD
A["処理開始"] --> B{"WMIサービスへの接続"};
B --> C{"WMIクラスの選択"};
C --> D{"WQLクエリの構築"};
D --> E["WMIからデータ取得"];
E --> F{"取得データの処理"};
F --> G["結果の表示/保存"];
G --> H["処理終了"];
subgraph 性能チューニング
E -- |クエリの最適化| E;
F -- |配列に格納| F_array["メモリ上の配列"];
F_array -- |一括書き込み| G;
B -- |ScreenUpdating無効化| B;
end
WMIサービスへの接続は、GetObject("winmgmts:\\.\root\cimv2") の形式で行います。これにより、ローカルマシンの標準的なWMIネームスペース(root\cimv2)に接続します。情報の種類に応じて、Win32_ComputerSystem、Win32_OperatingSystem、Win32_NetworkAdapterConfigurationなどのWMIクラスを選択し、Select文を含むWQLクエリでデータを抽出します。取得したデータは通常、SWbemObjectオブジェクトのコレクションとして返されるため、各プロパティにアクセスして情報を抽出します。
考慮事項
権限: WMIへのアクセスには適切な権限が必要です。通常、ローカル管理者権限があれば問題ありません。
エラーハンドリング: WMIサービスが利用できない、クエリが不正などの場合に備え、エラー処理を実装します。
性能: 大量の情報を頻繁に取得する場合、パフォーマンスが問題となる可能性があります。
実装
以下に、Excel VBAでWindowsシステム情報を取得する具体的なコード例を2つ示します。
1. 基本的なシステム情報取得(Excel)
このコードは、OS情報とコンピューター情報をExcelシートに書き出します。
' VBAとWMIによる基本的なシステム情報取得(Excel)
' ----------------------------------------------------------------------------------------------------
' [機能]
' Windowsの基本的なシステム情報(OS、CPU、RAMなど)をExcelシートに出力します。
' パフォーマンス向上のため、ScreenUpdatingを無効化しています。
' [前提条件]
' Excelが実行可能な環境であること。
' WMIサービスが動作していること。
' [入力]
' なし(現在のシステム情報を取得)。
' [出力]
' アクティブなExcelシートのA列、B列に「項目名」「値」の形式でシステム情報。
' [計算量]
' WMIクエリの複雑さおよび取得される情報量に依存しますが、通常はO(N) (Nは取得されるプロパティ数)。
' データが少ないため、ほぼ定数時間O(1)と見なせる。
' [メモリ条件]
' WMIオブジェクトと取得データを格納するためのごくわずかなメモリ。
' ----------------------------------------------------------------------------------------------------
Sub GetBasicSystemInfoToExcel()
Dim objWMIService As Object
Dim colItems As Object
Dim objItem As Object
Dim ws As Worksheet
Dim rowNum As Long
Dim startTime As Double
Dim endTime As Double
' パフォーマンス計測開始
startTime = Timer
' 画面更新を停止し、計算方法を手動に設定(性能チューニング)
Application.ScreenUpdating = False ' 約50%〜90%の処理時間短縮
Application.Calculation = xlCalculationManual ' 大量の数式計算がない限り影響は小さい
Set ws = ThisWorkbook.Sheets(1)
ws.Cells.ClearContents ' 既存の内容をクリア
rowNum = 1
ws.Cells(rowNum, 1).Value = "項目"
ws.Cells(rowNum, 2).Value = "値"
ws.Cells(rowNum, 1).Font.Bold = True
ws.Cells(rowNum, 2).Font.Bold = True
rowNum = rowNum + 1
On Error GoTo ErrorHandler
' WMIサービスへの接続
' URL: https://learn.microsoft.com/en-us/windows/win32/wmisdk/scripting-with-wmi (2024年7月26日アクセス, Microsoft)
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
' --- Win32_OperatingSystem クラスから情報を取得 ---
ws.Cells(rowNum, 1).Value = "--- OS情報 ---"
ws.Cells(rowNum, 1).Font.Bold = True
rowNum = rowNum + 1
' URL: https://learn.microsoft.com/en-us/windows/win32/cimwin32pkg/win32-operatingsystem (2024年7月26日アクセス, Microsoft)
Set colItems = objWMIService.ExecQuery("SELECT Caption, CSDVersion, OSArchitecture, Version, BuildNumber, LastBootUpTime FROM Win32_OperatingSystem")
For Each objItem In colItems
ws.Cells(rowNum, 1).Value = "OS名"
ws.Cells(rowNum, 2).Value = objItem.Caption
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "Service Pack"
ws.Cells(rowNum, 2).Value = objItem.CSDVersion
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "OSアーキテクチャ"
ws.Cells(rowNum, 2).Value = objItem.OSArchitecture
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "OSバージョン"
ws.Cells(rowNum, 2).Value = objItem.Version
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "ビルド番号"
ws.Cells(rowNum, 2).Value = objItem.BuildNumber
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "最終起動日時"
ws.Cells(rowNum, 2).Value = ConvertWMIDateTime(objItem.LastBootUpTime)
rowNum = rowNum + 1
Next
' --- Win32_ComputerSystem クラスから情報を取得 ---
ws.Cells(rowNum, 1).Value = "--- コンピューター情報 ---"
ws.Cells(rowNum, 1).Font.Bold = True
rowNum = rowNum + 1
' URL: https://learn.microsoft.com/en-us/windows/win32/cimwin32pkg/win32-computersystem (2024年7月26日アクセス, Microsoft)
Set colItems = objWMIService.ExecQuery("SELECT Manufacturer, Model, Name, TotalPhysicalMemory FROM Win32_ComputerSystem")
For Each objItem In colItems
ws.Cells(rowNum, 1).Value = "製造元"
ws.Cells(rowNum, 2).Value = objItem.Manufacturer
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "モデル名"
ws.Cells(rowNum, 2).Value = objItem.Model
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "コンピューター名"
ws.Cells(rowNum, 2).Value = objItem.Name
rowNum = rowNum + 1
ws.Cells(rowNum, 1).Value = "総物理メモリ (GB)"
' WMIはバイト単位で返すため、GBに変換
ws.Cells(rowNum, 2).Value = Round(objItem.TotalPhysicalMemory / (1024 ^ 3), 2)
rowNum = rowNum + 1
Next
ws.Columns("A:B").AutoFit ' 列幅を自動調整
FinallyExit:
' 画面更新と計算方法を元に戻す
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
' オブジェクトの解放
Set objItem = Nothing
Set colItems = Nothing
Set objWMIService = Nothing
' パフォーマンス計測終了
endTime = Timer
ws.Cells(rowNum, 1).Value = "処理時間:"
ws.Cells(rowNum, 2).Value = Format(endTime - startTime, "0.00") & " 秒"
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
GoTo FinallyExit
End Sub
' WMIのDateTime形式をVBAのDate型に変換するヘルパー関数
Function ConvertWMIDateTime(strWMIDate As String) As Date
On Error Resume Next
ConvertWMIDateTime = CDate(Left(strWMIDate, 4) & "/" & Mid(strWMIDate, 5, 2) & "/" & Mid(strWMIDate, 7, 2) & _
" " & Mid(strWMIDate, 9, 2) & ":" & Mid(strWMIDate, 11, 2) & ":" & Mid(strWMIDate, 13, 2))
If Err.Number <> 0 Then
ConvertWMIDateTime = CDate("1900/1/1") ' エラー時のデフォルト値
End If
On Error GoTo 0
End Function
2. ネットワークアダプター情報取得と性能チューニング(Access/Excel両対応)
このコードは、ネットワークアダプターの情報を取得し、配列バッファを使用してAccessのテーブルまたはExcelシートに一括で書き込みます。個別セル/フィールド書き込みと比較して数倍から数十倍高速です。
' VBAとWMIによるネットワークアダプター情報取得と性能チューニング
' ----------------------------------------------------------------------------------------------------
' [機能]
' Windowsのネットワークアダプター情報を取得し、配列バッファを介してExcelシートまたはAccessテーブルに一括出力します。
' 一括書き込みにより、個々のセル/フィールドへの書き込みよりも大幅な性能向上が期待できます。
' [前提条件]
' Excelの場合:アクティブなワークシートが存在すること。
' Accessの場合:'ネットワークアダプター情報'という名前のテーブルが存在すること(初回実行時に作成されます)。
' WMIサービスが動作していること。
' [入力]
' なし(現在のシステム情報を取得)。
' [出力]
' Excelの場合:アクティブなシートにネットワークアダプター情報が出力されます。
' Accessの場合:'ネットワークアダプター情報'テーブルに情報が追加されます。
' [計算量]
' WMIクエリの複雑さおよび取得されるアダプター数Nに依存します。O(N * P) (Pは取得されるプロパティ数)。
' 配列バッファへの格納は高速ですが、WMIからのデータ取得はアダプター数に比例します。
' [メモリ条件]
' 取得されるネットワークアダプターの数とプロパティ数に応じた配列のメモリ使用。
' 通常は数十KB程度と想定され、Officeアプリケーションの許容範囲内。
' ----------------------------------------------------------------------------------------------------
Sub GetNetworkAdapterInfoOptimized()
Dim objWMIService As Object
Dim colItems As Object
Dim objItem As Object
Dim data() As Variant
Dim i As Long, j As Long
Dim colCount As Long
Dim startTime As Double
Dim endTime As Double
Dim currentApp As String
' 現在のアプリケーションを判定
On Error Resume Next
If TypeOf Application Is Excel.Application Then
currentApp = "Excel"
ElseIf TypeOf Application Is Access.Application Then
currentApp = "Access"
Else
MsgBox "このコードはExcelまたはAccessで実行してください。", vbExclamation
Exit Sub
End If
On Error GoTo 0
' パフォーマンス計測開始
startTime = Timer
' Excelの場合のみ画面更新を停止し、計算方法を手動に設定
If currentApp = "Excel" Then
Application.ScreenUpdating = False ' 約50%〜90%の処理時間短縮
Application.Calculation = xlCalculationManual ' 大量の数式計算がない限り影響は小さい
End If
On Error GoTo ErrorHandler
' WMIサービスへの接続
Set objWMIService = GetObject("winmgmts:\\.\root\cimv2")
' --- Win32_NetworkAdapterConfiguration クラスから情報を取得 ---
' Enabled = TRUE は有効なネットワークアダプタのみをフィルタリング
' URL: https://learn.microsoft.com/en-us/windows/win32/cimwin32pkg/win32-networkadapterconfiguration (2024年7月26日アクセス, Microsoft)
Set colItems = objWMIService.ExecQuery( _
"SELECT Description, IPAddress, MACAddress, DHCPEnabled, DHCPServer, DNSHostName, IPEnabled FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled = TRUE")
' ヘッダーとデータを格納する配列のサイズを決定
colCount = 7 ' プロパティ数: Description, IPAddress, MACAddress, DHCPEnabled, DHCPServer, DNSHostName, IPEnabled
i = 0
For Each objItem In colItems
i = i + 1 ' データの行数をカウント
Next
' データがない場合は終了
If i = 0 Then
MsgBox "有効なネットワークアダプター情報が見つかりませんでした。", vbInformation
GoTo FinallyExit
End If
' ヘッダー行 + データ行数で配列をリサイズ
ReDim data(1 To i + 1, 1 To colCount)
' ヘッダー行を設定
data(1, 1) = "説明"
data(1, 2) = "IPアドレス"
data(1, 3) = "MACアドレス"
data(1, 4) = "DHCP有効"
data(1, 5) = "DHCPサーバー"
data(1, 6) = "DNSホスト名"
data(1, 7) = "IP有効"
' データを配列に格納
i = 2 ' データは2行目から
For Each objItem In colItems
data(i, 1) = objItem.Description
' IPAddressは配列で返されるため、joinで結合
If Not IsNull(objItem.IPAddress) Then
data(i, 2) = Join(objItem.IPAddress, ", ")
Else
data(i, 2) = ""
End If
data(i, 3) = objItem.MACAddress
data(i, 4) = objItem.DHCPEnabled
data(i, 5) = objItem.DHCPServer
data(i, 6) = objItem.DNSHostName
data(i, 7) = objItem.IPEnabled
i = i + 1
Next
' 配列データを一括で出力
If currentApp = "Excel" Then
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(2) ' 2枚目のシートに出力
ws.Cells.ClearContents
ws.Range("A1").Resize(UBound(data, 1), UBound(data, 2)).Value = data
ws.Rows(1).Font.Bold = True ' ヘッダーを太字に
ws.Columns.AutoFit ' 列幅を自動調整
MsgBox "ネットワークアダプター情報をExcelシートに書き出しました。", vbInformation
ElseIf currentApp = "Access" Then
Dim db As Object ' DAO.Database
Dim rs As Object ' DAO.Recordset
Dim fld As Object ' DAO.Field
Dim td As Object ' DAO.TableDef
Set db = CurrentDb
' テーブルが存在しない場合は作成
On Error Resume Next
Set td = db.TableDefs("ネットワークアダプター情報")
If Err.Number <> 0 Then ' テーブルが存在しない
Err.Clear
Set td = db.CreateTableDef("ネットワークアダプター情報")
td.Fields.Append td.CreateField("説明", dbText, 255)
td.Fields.Append td.CreateField("IPアドレス", dbText, 255)
td.Fields.Append td.CreateField("MACアドレス", dbText, 255)
td.Fields.Append td.CreateField("DHCP有効", dbBoolean)
td.Fields.Append td.CreateField("DHCPサーバー", dbText, 255)
td.Fields.Append td.CreateField("DNSホスト名", dbText, 255)
td.Fields.Append td.CreateField("IP有効", dbBoolean)
db.TableDefs.Append td
MsgBox "「ネットワークアダプター情報」テーブルを作成しました。", vbInformation
End If
On Error GoTo ErrorHandler ' エラーハンドラを再有効化
' 既存データをクリアしてから追加
db.Execute "DELETE FROM [ネットワークアダプター情報];"
' レコードセットを開き、配列データを追加
Set rs = db.OpenRecordset("ネットワークアダプター情報", dbOpenTable)
For i = 2 To UBound(data, 1) ' ヘッダー行を除いて2行目から
rs.AddNew
For j = 1 To UBound(data, 2)
rs.Fields(data(1, j)).Value = data(i, j) ' ヘッダー名をフィールド名として使用
Next j
rs.Update
Next i
rs.Close
MsgBox "ネットワークアダプター情報をAccessテーブルに書き出しました。", vbInformation
End If
FinallyExit:
' Excelの場合のみ画面更新と計算方法を元に戻す
If currentApp = "Excel" Then
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End If
' オブジェクトの解放
Set objItem = Nothing
Set colItems = Nothing
Set objWMIService = Nothing
If currentApp = "Access" Then
Set rs = Nothing
Set db = Nothing
End If
' パフォーマンス計測終了
endTime = Timer
MsgBox "処理時間: " & Format(endTime - startTime, "0.00") & " 秒", vbInformation
Exit Sub
ErrorHandler:
MsgBox "エラーが発生しました: " & Err.Description, vbCritical
GoTo FinallyExit
End Sub
検証
取得した情報の検証は、Windowsに標準搭載されている以下のツールと比較することで行えます。
システム情報 (msinfo32.exe): OSのバージョン、ビルド番号、システムモデル、物理メモリなどの基本情報を確認できます。
dxdiag (DirectX診断ツール): コンピューター名、OSバージョン、プロセッサー情報など。
ipconfig /all: ネットワークアダプターの詳細情報(IPアドレス、MACアドレス、DHCP情報など)を確認できます。
タスクマネージャー: 物理メモリの総量を確認できます。
これらのツールとVBA/WMIで取得した値が一致することを確認することで、コードの正確性を検証できます。
運用
実行手順
VBAプロジェクトを開く: ExcelやAccessのファイルを開き、
Alt + F11キーを押してVBAエディターを開きます。モジュール挿入: プロジェクトエクスプローラーで該当するブック/データベースを右クリックし、「挿入」→「標準モジュール」を選択します。
コード貼り付け: 上記のVBAコードを新しいモジュールに貼り付けます。
マクロ実行: VBAエディターで
Subプロシージャ内にカーソルを置き、F5キーを押すか、Excel/Accessのリボンから「開発」タブ→「マクロ」を選択して実行します。Excelの場合、
GetBasicSystemInfoToExcelはアクティブシートの1枚目、GetNetworkAdapterInfoOptimizedは2枚目のシートに出力されます。Accessの場合、
GetNetworkAdapterInfoOptimizedは「ネットワークアダプター情報」というテーブルを作成(またはクリア)してデータを出力します。
ロールバック方法
VBAコードの削除: VBAエディターで、挿入した標準モジュールを右クリックし、「削除」を選択します。モジュールをエクスポートしてバックアップを取るか尋ねられたら、「いいえ」を選択します。
Excelシートのクリア/削除: Excelの場合、コードが書き込んだシートの内容をクリアするか、シート自体を削除します。
Accessテーブルの削除: Accessの場合、作成された「ネットワークアダプター情報」テーブルをデータベースウィンドウから削除します。
落とし穴
権限不足: WMIサービスへのアクセスには管理者権限が必要です。ユーザーが管理者権限を持たない場合、エラーが発生します。
WMIサービスの停止: WMIサービスがWindows上で停止している場合、
GetObjectが失敗します。WQLクエリの誤り: WQLの構文エラーや、存在しないWMIクラス/プロパティを指定すると、データが取得できません。
データ型の不一致: WMIから取得されるデータ型とVBAのデータ型との間で変換エラーが発生する場合があります(例:
LastBootUpTimeは特殊なWMI DateTime形式で返されるため、変換関数が必要です)。パフォーマンスボトルネック: 大量のデータを繰り返し取得する場合や、
ScreenUpdatingの無効化、配列バッファの使用などの最適化を行わない場合、処理が非常に遅くなる可能性があります。特に、ループ内でセルやフィールドに直接書き込む方法は避けるべきです。Null値の処理: WMIプロパティにはNull値が含まれることがあります。VBAでこれらを扱う際には
IsNull()関数でチェックする必要があります。リモートWMI: リモートPCのWMI情報を取得する場合は、ファイアウォールの設定やDCOMセキュリティ設定など、さらに複雑な設定が必要です。本記事のコードはローカルPC (
\\.\) を対象としています。
まとめ
VBAとWMIを組み合わせることで、Windowsシステムに関する豊富な情報をOfficeアプリケーション内でプログラム的に取得・活用することが可能です。本記事で紹介したように、適切なWMIクラスとWQLクエリを用いることで、OS、ハードウェア、ネットワーク構成など、多岐にわたるシステム情報を自動的に収集できます。
性能面では、Application.ScreenUpdatingの無効化や、データを配列に格納してから一括で書き込む手法が非常に効果的です。これにより、数千行規模のデータ処理においても、個別のセル/フィールドアクセスに比べて大幅な時間短縮(Excelで数秒〜数分 vs 数十ミリ秒)が期待できます。運用においては、権限、WMIサービスの稼働状況、WQLクエリの正確性、そしてNull値への対応に注意を払うことが重要です。これらのポイントを押さえることで、VBAとWMIによる強力なOffice自動化ソリューションを構築できます。

コメント