ねらい:VBAからWMIを呼び出し、PCの「見えない情報」を一気に可視化する
WMI(Windows Management Instrumentation)は、OS・ハードウェア・ネットワーク・プロセス・サービスなどの情報を標準化して提供する仕組みです。VBAからは GetObject(“winmgmts:\.\root\cimv2”) を起点にクエリを投げ、返ってきたオブジェクトを走査して値を取り出します。ここでは参照設定不要(Late Binding)で貼って動くテンプレートを、基本の呼び方、よく使うクラス、表への一括書き出し、トラブル対策の順に解説します。
重要ポイントの深掘り
WMIは「クエリを減らして一括取得」が速さの鍵です。1件ずつ取りに行くより、必要な列をまとめてSELECTしてコレクションを一度だけ走査します。企業環境では権限やポリシーで取得に失敗することがあるため、On Errorのスコープを最小にしつつ、空欄返しで“レポートは止めない”設計にしておくと実務で強いです。
基本形:WMIサービスを握って、SELECTで値を読む
サービス取得と簡易ユーティリティ
' ModWmiBase.bas
Option Explicit
Public Function WmiSvc(Optional ByVal ns As String = "root\cimv2") As Object
Set WmiSvc = GetObject("winmgmts:\\.\\" & ns)
End Function
Public Function WmiQuery(ByVal q As String, Optional ByVal ns As String = "root\cimv2") As Object
Set WmiQuery = WmiSvc(ns).ExecQuery(q)
End Function
VBOS・CPU・メモリを最短で取る
' ModWmiQuick.bas
Option Explicit
Public Function OsCaption() As String
Dim it As Object
For Each it In WmiQuery("SELECT Caption FROM Win32_OperatingSystem")
OsCaption = it.Caption: Exit For
Next
End Function
Public Function CpuName() As String
Dim it As Object
For Each it In WmiQuery("SELECT Name FROM Win32_Processor")
CpuName = it.Name: Exit For
Next
End Function
Public Function TotalRamGB() As Double
Dim it As Object
For Each it In WmiQuery("SELECT TotalVisibleMemorySize FROM Win32_OperatingSystem")
TotalRamGB = Round(it.TotalVisibleMemorySize / 1024# / 1024#, 2): Exit For
Next
End Function
VB重要ポイントの深掘り
同じクラスを何度も問い合わせると遅くなります。必要列を1本のSELECTにまとめる、またはクエリ結果をローカル変数へキャッシュして使い回すと速度が安定します。
よく使うWMIクラス:ドライブ・ネットワーク・プロセス・サービス
ドライブの空き容量と総容量(固定ディスクのみ)
' ModWmiDrive.bas
Option Explicit
Public Function DriveTable() As Variant
Dim col As Object: Set col = WmiQuery("SELECT Name, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType=3")
Dim cnt As Long: cnt = 0
Dim it As Object: For Each it In col: cnt = cnt + 1: Next
Dim a() As Variant: ReDim a(1 To cnt + 1, 1 To 3)
a(1, 1) = "Drive": a(1, 2) = "Free(GB)": a(1, 3) = "Size(GB)"
Dim i As Long: i = 2
For Each it In col
a(i, 1) = it.Name
a(i, 2) = Round(CDbl(it.FreeSpace) / 1024# / 1024# / 1024#, 2)
a(i, 3) = Round(CDbl(it.Size) / 1024# / 1024# / 1024#, 2)
i = i + 1
Next
DriveTable = a
End Function
VBIPアドレス(有効NICのみ、IPv4を抽出)
' ModWmiNet.bas
Option Explicit
Public Function IPv4List() As String
Dim res As String, it As Object
For Each it In WmiQuery("SELECT IPAddress FROM Win32_NetworkAdapterConfiguration WHERE IPEnabled=TRUE")
If Not IsNull(it.IPAddress) Then
Dim ip: For Each ip In it.IPAddress
If InStr(CStr(ip), ":") = 0 Then res = res & ip & " "
Next
End If
Next
IPv4List = Trim$(res)
End Function
VB実行中プロセスの一覧(名前とPID)
' ModWmiProcess.bas
Option Explicit
Public Function ProcessTable(Optional ByVal topN As Long = 50) As Variant
Dim col As Object: Set col = WmiQuery("SELECT Name, ProcessId FROM Win32_Process")
Dim cnt As Long: cnt = 0: Dim it As Object
For Each it In col: cnt = cnt + 1: If cnt >= topN Then Exit For: Next
Dim a() As Variant: ReDim a(1 To WorksheetFunction.Min(cnt, topN) + 1, 1 To 2)
a(1, 1) = "Name": a(1, 2) = "PID"
Dim i As Long: i = 2
For Each it In col
a(i, 1) = it.Name
a(i, 2) = it.ProcessId
i = i + 1
If i > UBound(a, 1) Then Exit For
Next
ProcessTable = a
End Function
VBサービスの状態(自動起動で停止しているものの抽出にも使える)
' ModWmiService.bas
Option Explicit
Public Function ServiceTable() As Variant
Dim col As Object: Set col = WmiQuery("SELECT Name, DisplayName, State, StartMode FROM Win32_Service")
Dim cnt As Long: cnt = 0: Dim it As Object
For Each it In col: cnt = cnt + 1: Next
Dim a() As Variant: ReDim a(1 To cnt + 1, 1 To 4)
a(1, 1) = "Name": a(1, 2) = "DisplayName": a(1, 3) = "State": a(1, 4) = "StartMode"
Dim i As Long: i = 2
For Each it In col
a(i, 1) = it.Name
a(i, 2) = it.DisplayName
a(i, 3) = it.State
a(i, 4) = it.StartMode
i = i + 1
Next
ServiceTable = a
End Function
VB重要ポイントの深掘り
ネットワークやサービスは環境依存で取得失敗が起きやすい領域です。値がNullのときは空文字で受け止め、テーブル化しても崩れないようにしておくと現場で止まりません。
便利関数:WMI時刻の変換・台数のカウント・フィルタ
WMI時刻(yyyymmddHHMMSS.******±TZ)をISOへ
' ModWmiTime.bas
Option Explicit
Public Function WmiTimeToIso(ByVal s As String) As String
If Len(s) >= 14 Then
WmiTimeToIso = Left$(s, 4) & "-" & Mid$(s, 5, 2) & "-" & Mid$(s, 7, 2) & " " & Mid$(s, 9, 2) & ":" & Mid$(s, 11, 2) & ":" & Mid$(s, 13, 2)
End If
End Function
Public Function LastBootUpTimeIso() As String
Dim it As Object
For Each it In WmiQuery("SELECT LastBootUpTime FROM Win32_OperatingSystem")
LastBootUpTimeIso = WmiTimeToIso(it.LastBootUpTime): Exit For
Next
End Function
VBテーブルフィルタ(状態で絞る例)
' ModFilter.bas
Option Explicit
Public Function FilterServicesByState(ByVal state As String) As Variant
Dim col As Object: Set col = WmiQuery("SELECT Name, State FROM Win32_Service WHERE State='" & state & "'")
Dim cnt As Long: cnt = 0: Dim it As Object
For Each it In col: cnt = cnt + 1: Next
Dim a() As Variant: ReDim a(1 To cnt + 1, 1 To 2)
a(1, 1) = "Name": a(1, 2) = "State"
Dim i As Long: i = 2
For Each it In col
a(i, 1) = it.Name
a(i, 2) = it.State
i = i + 1
Next
FilterServicesByState = a
End Function
VBレポート生成テンプレート:一括収集→シートへ書く
入口と書き出し(貼ってすぐ動く)
' ModWmiReport.bas
Option Explicit
Public Sub Run_WmiReport()
On Error GoTo EH
AppEnter "WMI Report"
Dim ws As Worksheet
On Error Resume Next
Set ws = ThisWorkbook.Worksheets("WMI")
On Error GoTo 0
If ws Is Nothing Then
Set ws = ThisWorkbook.Worksheets.Add
ws.Name = "WMI"
End If
ws.Cells.Clear
Dim row As Long: row = 1
ws.Cells(row, 1).Value = "OS": ws.Cells(row, 2).Value = OsCaption(): row = row + 1
ws.Cells(row, 1).Value = "CPU": ws.Cells(row, 2).Value = CpuName(): row = row + 1
ws.Cells(row, 1).Value = "Total RAM(GB)":ws.Cells(row, 2).Value = TotalRamGB(): row = row + 1
ws.Cells(row, 1).Value = "LastBootUp": ws.Cells(row, 2).Value = LastBootUpTimeIso(): row = row + 2
Dim drives As Variant: drives = DriveTable()
ws.Range("A" & row).Resize(UBound(drives, 1), UBound(drives, 2)).Value = drives
row = row + UBound(drives, 1) + 1
Dim ips As String: ips = IPv4List()
ws.Cells(row, 1).Value = "IPv4": ws.Cells(row, 2).Value = ips: row = row + 2
Dim svcs As Variant: svcs = ServiceTable()
ws.Range("A" & row).Resize(UBound(svcs, 1), UBound(svcs, 2)).Value = svcs
ws.Columns.AutoFit
AppLeave
MsgBox "WMIレポートを作成しました。", vbInformation
Exit Sub
EH:
AppLeave
MsgBox "取得に失敗しました: " & Err.Description, vbExclamation
End Sub
VB共通枠(描画・イベント抑止と復帰)
' ModApp.bas
Option Explicit
Public Sub AppEnter(Optional ByVal status As String = "")
Application.ScreenUpdating = False
Application.EnableEvents = False
Application.DisplayAlerts = False
If Len(status) > 0 Then Application.StatusBar = status
End Sub
Public Sub AppLeave()
Application.StatusBar = False
Application.DisplayAlerts = True
Application.EnableEvents = True
Application.ScreenUpdating = True
End Sub
VB重要ポイントの深掘り
「配列へまとめて→Resizeで一括書き込み」がExcelを固めないコツです。セルに1件ずつ書くと遅くなるため、必ず配列I/Oを使いましょう。開始・終了枠で描画とイベントを止めると、数千行でも快適に終わります。
実務で効くトラブルシュート活用例
遅い・不安定の切り分けに使う視点
- 低容量ドライブ(Free(GB)が閾値未満)を特定することで、保存失敗やフリーズの予兆を把握できます。
- RAMが少ない端末(Total RAMが小さい)で巨大ブックを扱っていないか確認できます。
- LastBootUpが古い端末は再起動の提案が合理的です。
- IPv4が空欄ならネットワーク切断の可能性。サービス一覧で自動起動なのに停止中のものを見つけると、関連機能の不具合原因が絞れます。
より深い取得:イベントログ・プリンタ・BIOS・バッテリ(必要に応じて)
イベントログ(最近のエラーのみ抜粋)
' ModWmiEvent.bas
Option Explicit
Public Function RecentSystemErrors(Optional ByVal takeN As Long = 20) As Variant
Dim q As String
q = "SELECT TimeGenerated, SourceName, EventIdentifier FROM Win32_NTLogEvent WHERE Logfile='System' AND Type='Error'"
Dim col As Object: Set col = WmiQuery(q)
Dim buf() As Variant: ReDim buf(1 To takeN + 1, 1 To 3)
buf(1, 1) = "Time": buf(1, 2) = "Source": buf(1, 3) = "EventId"
Dim i As Long: i = 2, it As Object
For Each it In col
If i > UBound(buf, 1) Then Exit For
buf(i, 1) = WmiTimeToIso(it.TimeGenerated)
buf(i, 2) = it.SourceName
buf(i, 3) = it.EventIdentifier
i = i + 1
Next
RecentSystemErrors = buf
End Function
VBプリンタ一覧
' ModWmiPrinter.bas
Option Explicit
Public Function PrinterTable() As Variant
Dim col As Object: Set col = WmiQuery("SELECT Name, Default, WorkOffline FROM Win32_Printer")
Dim cnt As Long: cnt = 0: Dim it As Object
For Each it In col: cnt = cnt + 1: Next
Dim a() As Variant: ReDim a(1 To cnt + 1, 1 To 3)
a(1, 1) = "Name": a(1, 2) = "Default": a(1, 3) = "Offline"
Dim i As Long: i = 2
For Each it In col
a(i, 1) = it.Name
a(i, 2) = it.Default
a(i, 3) = it.WorkOffline
i = i + 1
Next
PrinterTable = a
End Function
VB落とし穴と対策(深掘り)
権限・ポリシーで失敗する
企業環境ではWMIクエリが制限されることがあります。失敗時はOn Errorのスコープを最小に、空欄返しでレポートを継続。どうしても必要ならPowerShell(Get-WmiObject / Get-CimInstance)へ委譲し、結果CSVをVBAで読む設計が堅牢です。
遅い・タイムアウトする
一回の実行で大量の行を取るクエリは重くなります。列選択を絞る、WHEREで対象を限定する、巨大クラス(NTLogEventなど)は件数をtakeNで抑えるなど、クエリ自体を軽くしましょう。
Nullと型の扱い
WMIはNullがよく返ります。文字列へ取り込む前にIsNullをチェックし、数値はCDblで明示変換。配列へ詰める際に空文字や0で統一しておくと書き込み時に崩れません。
まとめ:Late Binding+配列I/O+一括クエリで「速い・止まらない」WMIレポートを作る
GetObjectでWMIに接続し、必要列をまとめてSELECTして結果を配列へ一括格納、シートへResize書き込み。これだけでOS・CPU・メモリ・ドライブ・ネット・サービスの「環境台帳」が数秒で作れます。
