在 C#・PowerShell 中使用 WMI/CIM ── 硬體資訊取得・處理程序監控・遠端查詢的實務指南
· Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, 業務應用程式, Windows 開發
「想在業務應用程式的畫面上顯示 PC 的序號與機型名稱」「想監控伺服器的磁碟可用空間並發出警告」「想偵測某個特定處理程序何時啟動」「想彙整查詢遠端 PC 的狀態」── 在 Windows 業務應用程式與管理工具的開發中,這類需求是定番。而這個定番需求的標準答案,就是WMI(Windows Management Instrumentation),若以標準名稱來說則是CIM(Common Information Model)。
麻煩的地方在於,WMI 相關的資訊新舊混雜。搜尋時會同時出現使用 Get-WmiObject 的 10 年前文章,以及使用 Get-CimInstance 的文章;C# 這邊也有 System.Management 與 Microsoft.Management.Infrastructure 兩種體系。哪一種是現行寫法、哪一種是「現在仍能動,但不建議在新專案中選用」的寫法,很難分辨清楚。實際上,Get-WmiObject 在 PowerShell 7 中並不存在,這個問題常在把針對 5.1 撰寫的公司內部指令碼進行遷移時突然浮現。
本文以在業務應用程式中實作硬體資訊取得、處理程序監控、遠端 PC 查詢的 C#/PowerShell 開發者為對象,從 WMI/CIM 結構的最低限度理解開始,整理 PowerShell 的 CIM Cmdlet、C# 的兩種 API、常用實例配方、效能・權限・64bit 方面的陷阱,直到「不該使用 WMI 的場合」的判斷標準,內容以 2026 年 8 月時點的第一手資料為依據。
1. 先講結論
- CIM 是 DMTF 制定的管理資訊業界標準,WMI 則是其 Microsoft 實作。PowerShell 與 C# 的「CIM」系列 API,是遵循這套標準的現行世代 API,連接的對象仍是同一個 WMI 基礎架構。1
- PowerShell 現行做法是 CIM Cmdlet(Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent)。舊版 WMI Cmdlet(Get-WmiObject 等 5 個)已在 PowerShell 6 以後被移除,PowerShell 7 中無法使用。2
- 預設的命名空間是 root/CIMV2,日常查詢基本上是在此以 WQL 篩選 Win32_* 類別。3
- 遠端查詢預設使用 WSMan(WinRM)。指定
-ComputerName會建立 WSMan 的臨時工作階段。若要對同一對象多次查詢,重複使用 CIM 工作階段(New-CimSession)是效能上的標準做法;對於無法設定 WinRM 的舊型對象,則有 DCOM 通訊協定選項可用。34 - C# 有 System.Management(ManagementObjectSearcher)與 Microsoft.Management.Infrastructure(CimSession)兩種體系。兩者都僅限 Windows 使用,在現行 .NET 中皆透過 NuGet 引入。若要正式進行遠端或監控,適合使用與 CIM Cmdlet 相同型別體系的 MI API。56
- 偵測處理程序啟動應使用事件訂閱,不要用輪詢。訂閱
Win32_ProcessStartTrace必須以系統管理員權限執行。78 - 不要惰性地使用
SELECT *。用-Filter/-Property/-KeyOnly縮小要傳輸的資料,能預防一半的 WMI 效能問題。3 - WMI 並非萬能。高頻率的效能監控、自家應用程式的設定讀寫、單次的 OS 功能呼叫,用效能計數器・登錄檔・Win32 API・專用 Cmdlet 更為合適(詳見第 8 章的判斷表)。
2. WMI/CIM 是什麼 ── 標準與實作、命名空間、類別、WQL
首先一次整理清楚各個術語之間的關係。
| 術語 | 真面目 |
|---|---|
| CIM(Common Information Model) | 用來表示系統・應用程式・網路・裝置等管理對象的業界標準模型。由 DMTF(Distributed Management Task Force)制定・維護1 |
| WBEM(Web-Based Enterprise Management) | 為存取企業環境管理資訊而制定標準技術的業界倡議1 |
| WMI | WBEM 的Microsoft 實作。使用 CIM 標準來表示管理對象,內建於 Windows1 |
| MI(Windows Management Infrastructure) | WMI 的次世代版本。與傳統的 WMI 完全相容,許多新的提供者(provider)都以 MI 撰寫1 |
身為開發者應該掌握的結構,有以下 4 項。
- 命名空間(namespace):彙整類別的階層結構。日常查詢幾乎都使用 root/CIMV2,這也是 CIM Cmdlet 的預設值。3 其他還有
root\default(登錄檔提供者等)等命名空間。 - 類別:像是
Win32_ComputerSystem(電腦本體)、Win32_LogicalDisk(邏輯磁碟機)、Win32_Process(處理程序)這類管理對象的型別。繼承自 CIM 標準類別(CIM_LogicalDisk等)的 Windows 專屬類別會帶有Win32_前綴。9 - 提供者(provider):提供類別實體的元件。查詢時,提供者會即時向 OS 詢問並產生值。
- WQL:類似 SQL 的查詢語言。像
SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto'這樣,把類別當成資料表來篩選。CIM Cmdlet 預設的查詢語言也是 WQL。3
「能用統一的類別與查詢語言讀取 OS 與硬體的資訊」── 這正是 WMI 的價值所在。反過來說,寫入或操作僅限於部分具有可透過 Invoke-CimMethod 呼叫方法的類別,並非什麼都能做的萬能機制。
3. 從 PowerShell 使用 ── CIM Cmdlet 為現行做法,WMI Cmdlet 已遭移除
3.1. 基本用法是 Get-CimInstance
# 指定類別(使用預設命名空間 root/CIMV2)
Get-CimInstance -ClassName Win32_OperatingSystem
# 只在 -Filter 中寫 WHERE 子句的內容(不要寫 WHERE 關鍵字)
Get-CimInstance -ClassName Win32_Service -Filter "StartMode = 'Auto' AND State <> 'Running'"
# 只取得需要的屬性,以減少傳輸量
Get-CimInstance -ClassName Win32_Process -Property Name, ProcessId, CreationDate
# 若要直接寫 WQL,使用 -Query
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE Name LIKE 'p%'"
-Filter 就是 WQL 的 WHERE 子句本身,-Property 則用於限定要取得的欄位。3 傳回值是 CimInstance 物件,日期屬性(CreationDate、LastBootUpTime 等)會以已轉換成 DateTime 的形式傳回。與舊版 Get-WmiObject 不同,取得的物件不直接具備方法,因此呼叫方法要透過 Invoke-CimMethod。
# 呼叫執行個體的方法: 取得各處理程序的擁有者
Get-CimInstance -ClassName Win32_Process -Filter "Name = 'notepad.exe'" |
Invoke-CimMethod -MethodName GetOwner
# 呼叫類別的靜態方法: 啟動處理程序
Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = 'notepad.exe' }
# 查詢類別定義(屬性・方法一覽)
Get-CimClass -ClassName Win32_Process
3.2. 從舊版 WMI Cmdlet 遷移的對照表
在 PowerShell 6 以後(現行的 PowerShell 7)中,以下的 WMI v1 Cmdlet 已遭到移除。相同的功能由 CimCmdlets 模組(WMI v2)提供。2
| 舊版(僅限 Windows PowerShell 5.1) | 現行(CIM Cmdlet) | 補充 |
|---|---|---|
Get-WmiObject |
Get-CimInstance |
-Filter / -Query 的概念相同 |
Get-WmiObject -List |
Get-CimClass |
用於探索類別・確認定義 |
Invoke-WmiMethod |
Invoke-CimMethod |
引數以 -Arguments @{ } 這種雜湊表傳遞 |
Register-WmiEvent |
Register-CimIndicationEvent |
事件訂閱(6.3 節) |
Set-WmiInstance |
Set-CimInstance |
變更可寫入的屬性 |
Remove-WmiObject |
Remove-CimInstance |
刪除執行個體 |
Windows PowerShell 5.1 中也可以使用 CIM Cmdlet,因此新撰寫的內容,即使要在 5.1 上執行,也用 CIM 一側撰寫,這樣才不會留下遷移成本。5.1 與 7 共存・遷移的整體樣貌,整理在「Windows PowerShell 5.1 與 PowerShell 7 的差異」一文中。
4. 遠端查詢 ── CIM 工作階段(WSMan 為預設)與 DCOM 選項
CIM Cmdlet 在未指定時,會以 COM 方式連接本機的 WMI;若指定 -ComputerName,則會透過WSMan(WinRM)通訊協定建立臨時工作階段來連線。若要對同一台電腦執行多次操作,建立 CIM 工作階段並重複使用會在效能上更有利。3
# 單次查詢用 -ComputerName(每次都會建立臨時工作階段)
Get-CimInstance -ClassName Win32_ComputerSystem -ComputerName Server01, Server02
# 若要重複查詢,重複使用 CIM 工作階段
$session = New-CimSession -ComputerName Server01
Get-CimInstance -ClassName Win32_OperatingSystem -CimSession $session
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" -CimSession $session
Remove-CimSession $session
對於無法設定 WinRM 的舊型電腦等、無法用 WSMan 連線的對象,可以選擇DCOM 通訊協定。4
$dcom = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName OldServer -SessionOption $dcom
遠端查詢的前提條件如下。
- 連接目標已設定 WinRM。
winrm quickconfig會一併完成服務自動啟動化・HTTP 接聽器(預設連接埠 5985)的建立・防火牆例外的登錄。10 若想透過 HTTPS(預設連接埠 5986)連線,光靠這樣還不夠,需要另外準備伺服器憑證,再用winrm quickconfig -transport:https等方式單獨設定 HTTPS 接聽器。10 - 路徑上的防火牆已開放對應連接埠。輸入規則的設計與登錄實務,在「Windows 防火牆與業務應用程式」一文中已有處理。
- 驗證。在網域環境中,會以 Kerberos 進行相互驗證。工作群組中因無法使用 Kerberos,有時需要在用戶端的
TrustedHosts中登錄。登錄對象應盡量限縮到必要的最小範圍。10 - 權限。在預設設定下,遠端的 WMI 查詢・操作基本上須以隸屬於連接目標系統管理員群組的帳戶進行。若要開放給一般使用者,則需要同時在 WinRM 與 WMI 命名空間兩方設定存取權限。10
- 另外,由於 DCOM 沒有固定的接聽連接埠(使用 RPC 的動態連接埠),要設計跨越防火牆的架構會比較困難。今後新建的機制,建議還是以WSMan 為預設來考量較為穩妥。
5. 從 C# 使用 ── System.Management 與 Microsoft.Management.Infrastructure
從 C# 使用 WMI 的 API 有兩種體系。兩者都僅限 Windows 使用。
| System.Management | Microsoft.Management.Infrastructure (MI API) | |
|---|---|---|
| 引入方式 | .NET Framework 為標準內建。現行 .NET 中為 NuGet 套件 System.Management5 | NuGet 套件 Microsoft.Management.Infrastructure6 |
| 入口類別 | ManagementObjectSearcher(傳入 WQL 進行查詢)5 |
CimSession(Create → QueryInstances / InvokeMethod / Subscribe)6 |
| 型別體系 | ManagementObject / ManagementEventWatcher11 |
CimInstance / CimSession ── 與 CIM Cmdlet 相同的型別3 |
| 遠端 | 以 DCOM 為基礎 | 以 WSMan(CIM 工作階段)為基礎。有非同步版(*Async)6 |
| 適用場合 | 本機資訊取得。維護既有程式碼資產 | 導入遠端查詢・監控。搭配 PowerShell 的設計 |
5.1. System.Management: ManagementObjectSearcher 的基本用法
以字串傳入 WQL,再用 Get() 接收結果集合。5
// NuGet: System.Management(僅限 Windows)
using System.Management;
using var searcher = new ManagementObjectSearcher(
@"root\cimv2",
"SELECT DeviceID, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType = 3");
foreach (ManagementObject disk in searcher.Get())
{
var freeGb = (ulong)disk["FreeSpace"] / 1024.0 / 1024.0 / 1024.0;
var sizeGb = (ulong)disk["Size"] / 1024.0 / 1024.0 / 1024.0;
Console.WriteLine($"{disk["DeviceID"]} 可用 {freeGb:F1} GB / 總計 {sizeGb:F1} GB");
}
屬性以索引子的形式傳回 object,因此要對照類別文件確認 CIM 型別(此例中 FreeSpace / Size 是 uint649)後再進行轉型。若在此誤以為是 int 而直接轉型,就會遇到 InvalidCastException,這是最初常見的絆腳石。
5.2. MI API: CimSession 的基本用法
CimSession 可以用相同的方式處理本機與遠端。列舉・查詢・呼叫方法・事件訂閱,乃至非同步版,一應俱全。6
// NuGet: Microsoft.Management.Infrastructure(僅限 Windows)
using Microsoft.Management.Infrastructure;
// 本機用 CimSession.Create(null),遠端則傳入電腦名稱
using CimSession session = CimSession.Create(null);
IEnumerable<CimInstance> disks = session.QueryInstances(
@"root\cimv2", "WQL",
"SELECT DeviceID, FreeSpace, Size FROM Win32_LogicalDisk WHERE DriveType = 3");
foreach (CimInstance disk in disks)
{
var deviceId = (string)disk.CimInstanceProperties["DeviceID"].Value;
var free = (ulong)disk.CimInstanceProperties["FreeSpace"].Value;
Console.WriteLine($"{deviceId} 可用 {free / 1024.0 / 1024 / 1024:F1} GB");
}
因為處理的是與 PowerShell 的 CIM Cmdlet 傳回相同的 CimInstance,所以「先在 PowerShell 中試作,再改寫成 C#」這種開發流程能自然銜接。若要設計 C# 與 PowerShell 之間的整合本身,也請參考「在 C#(CSharp)中執行 PowerShell 並以物件形式接收結果」。
6. 常用實例配方
6.1. 常用類別速查表
| 想取得的資訊 | 類別 | 主要屬性 |
|---|---|---|
| 廠牌・機型名稱 | Win32_ComputerSystem |
Manufacturer, Model |
| 主機序號 | Win32_BIOS |
SerialNumber |
| OS 版本・開機時間 | Win32_OperatingSystem |
Caption, Version, LastBootUpTime |
| 磁碟可用容量 | Win32_LogicalDisk |
DeviceID, FreeSpace, Size, DriveType9 |
| 服務狀態 | Win32_Service |
Name, State, StartMode |
| 處理程序清單 | Win32_Process |
Name, ProcessId, CommandLine |
6.2. 資產管理的定番用法:序號・機型名稱・磁碟可用空間
# 機型資訊與序號(用於與 PC 資產台帳核對)
$cs = Get-CimInstance -ClassName Win32_ComputerSystem -Property Manufacturer, Model
$bios = Get-CimInstance -ClassName Win32_BIOS -Property SerialNumber
[pscustomobject]@{
Manufacturer = $cs.Manufacturer
Model = $cs.Model
Serial = $bios.SerialNumber
}
# 本機磁碟(DriveType = 3)的可用容量
Get-CimInstance -ClassName Win32_LogicalDisk -Filter "DriveType = 3" |
Select-Object DeviceID,
@{ Name = 'FreeGB'; Expression = { [math]::Round($_.FreeSpace / 1GB, 1) } },
@{ Name = 'SizeGB'; Expression = { [math]::Round($_.Size / 1GB, 1) } }
DriveType = 3 代表「本機磁碟」,可排除卸除式(2)、網路磁碟機(4)、光碟機(5)。9 若做監控,只要把這段指令碼透過 CIM 工作階段送到各台伺服器執行,就能建立起免代理程式的磁碟監控基礎。
6.3. 偵測處理程序啟動 ── 事件訂閱
不要「用輪詢的方式定期取得 Win32_Process 再比對差異」,而應改用事件訂閱。訂閱 Win32_ProcessStartTrace(核心追蹤提供者的事件類別,擁有 ProcessName / ProcessID / ParentProcessID 等屬性8)是最簡單的偵測處理程序啟動的方式。
# 請以系統管理員權限的 PowerShell 執行
$action = {
$name = $Event.SourceEventArgs.NewEvent.ProcessName
$id = $Event.SourceEventArgs.NewEvent.ProcessID
Write-Host "處理程序啟動: $name (PID=$id)"
}
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace `
-SourceIdentifier ProcessStarted -Action $action
訂閱會在執行此登錄動作的 PowerShell 工作階段存活期間持續有效,每當有處理程序啟動,-Action 就會執行一次。請注意,若把解除指令緊接著一次執行完畢,訂閱會在監控開始之前就被解除。解除應在結束監控時執行。
# 結束監控時: 解除訂閱
Unregister-Event -SourceIdentifier ProcessStarted
Register-CimIndicationEvent 會以類別名稱或 WQL 事件查詢登錄訂閱,每當有事件送達,就會執行 -Action 的指令碼區塊。7 訂閱這個類別需要系統管理員權限。7 誰能接收事件,是由事件類別的安全性描述元所控制,一般使用者維持原狀會遭到拒絕存取。8
另一種方法,是可用於任意類別的通用執行個體建立事件(__InstanceCreationEvent)。這種方式是由 WMI 依 WITHIN 指定的間隔進行輪詢、把差異轉成事件的機制,因此偵測間隔與負載之間的取捨,要由使用者自行決定。
# 以 5 秒間隔輪詢,監控 Win32_Process 的新執行個體
$query = "SELECT * FROM __InstanceCreationEvent WITHIN 5 WHERE TargetInstance ISA 'Win32_Process'"
Register-CimIndicationEvent -Query $query -SourceIdentifier ProcPoll -Action {
Write-Host "啟動: $($Event.SourceEventArgs.NewEvent.TargetInstance.Name)"
}
在 C#(System.Management)中,ManagementEventWatcher 扮演相同的角色。11
using System.Management;
// 在以系統管理員身分執行的處理程序中
var watcher = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
watcher.EventArrived += (_, e) =>
{
var name = (string)e.NewEvent["ProcessName"];
var pid = (uint)e.NewEvent["ProcessID"];
Console.WriteLine($"處理程序啟動: {name} (PID={pid})");
};
watcher.Start();
// 監控結束時別忘了呼叫 watcher.Stop() 與 Dispose
若要納入常駐監控的設計,請務必連同訂閱中斷時(服務重新啟動時・發生錯誤時)的重新登錄一併考慮進去。包含裝置監控在內的「狀態確認與顯示」設計論,在「外部設備狀態檢查與顯示的最佳實務 - 別只用『連線中』就交差的設計」一文中有所探討。
7. 陷阱 ── 效能・權限・64bit・儲存庫・日期
7.1. 濫用 SELECT * 與過度輪詢
WMI 的查詢是「提供者即時產生值」的處理程序,並非沒有成本。定番的反模式有兩種。
- 惰性使用
SELECT *。若對Win32_Process取得全部屬性的全部資料,提供者的工作量與網路傳輸量(遠端時)都會隨之膨脹。用-Filter篩選列、用-Property篩選欄,若只需要主鍵以供後續操作,則使用-KeyOnly。這些都是官方為了「縮小物件大小與網路流量」而提供的手段。3 - 短週期輪詢。「每秒執行一次
Get-CimInstance Win32_Process」這類設計,應改為 6.3 節的事件訂閱。若無論如何都要使用輪詢型(WITHIN),也應把間隔拉長到符合需求的必要程度。
此外,對遠端逐台以 -ComputerName 反覆呼叫也很浪費。因為每次查詢都會建立臨時工作階段,多次操作應改為重複使用 CIM 工作階段。3
7.2. 事件訂閱的權限
如 6.3 節所述,Win32_ProcessStartTrace 系列的訂閱以系統管理員權限為前提。7 「在開發機(以系統管理員身分執行)上能動,到客戶端的一般使用者環境卻無法監控」這種事故,和防火牆的通知對話方塊一樣是定番陷阱。若要在以一般使用者身分執行的業務應用程式中加入監控,建議把監控部分獨立成 Windows 服務(LocalSystem 等),再透過處理程序間通訊與應用程式本體連接。
7.3. 32bit/64bit 與提供者
在 64bit Windows 中,有些提供者同時存在 32bit 版與 64bit 版,預設會由與呼叫端應用程式位元數相符的一方回應。12 典型的例子是 root\default 的登錄檔提供者(StdRegProv),從 32bit 應用程式讀取時,傳回的會是 Wow6432Node 這一側(32bit 檢視)的值。12 若遇到「用 WMI 讀到的登錄檔值,和用 regedit 看到的值不一樣」,請優先懷疑這一點。若需要另一側的檢視,可以在連線內容中指定 __ProviderArchitecture(若要強制指定則再加上 __RequiredArchitecture)明確要求。12 位元數問題的整體樣貌,也在「在 C# 中安全呼叫 Win32 API — P/Invoke 實務指南(DllImport / LibraryImport / CsWin32)」中有所探討。
7.4. WMI 儲存庫損毀時的症狀與處理
WMI 的類別定義儲存在儲存庫中(並非單一檔案,而是由 Repository 資料夾內的一群檔案組成資料庫來運作13)。當這裡發生不一致時,即使應用程式端什麼都沒改,也會開始出現「應該存在的類別找不到」「命名空間無效」之類的錯誤。診斷與修復可以使用 winmgmt.exe。13
rem 完整性檢查(結果為 inconsistent 表示有不一致)
winmgmt /verifyrepository
rem 完整性檢查+若有不一致則重建(能讀取的內容會被合併)
winmgmt /salvagerepository
必須注意的是,不要把刪除或初始化儲存庫當成第一步。透過 WMI 出現的錯誤,有時源自 OS 的其他部分,Microsoft 明確指出,把刪除儲存庫當成第一個因應手段,「可能導致系統或已安裝應用程式受損」。13 請遵守先用 /verifyrepository 確認 → 再用 /salvagerepository 修復的順序。
7.5. DMTF 日期格式的轉換
WMI 的日期,是以 CIM 規格所定義的 DMTF 格式字串 yyyymmddHHMMSS.mmmmmm±UUU(結尾為相對 UTC 的偏移分鐘數。例如: 20260801100000.000000+540)儲存的。請不要對原始值進行字串拼接處理,而是使用轉換 API。
- C# (System.Management):
ManagementDateTimeConverter提供 DMTF 格式與DateTime/TimeSpan互相轉換的方法。11 - CIM 系列 API(Get-CimInstance / MI API): 日期屬性會以已轉換成
DateTime的形式傳回,因此原本就不會遇到這個問題。(Get-CimInstance Win32_OperatingSystem).LastBootUpTime可以直接以DateTime用於計算。
8. 不該使用 WMI 的場合 ── 手段判斷表
WMI 作為「統一的讀取入口」表現優異,但並非永遠是最佳解。以下是實務上該如何區分使用的參考標準。
| 想做的事 | 適合的手段 | 不選 WMI 的理由 |
|---|---|---|
| 取得硬體資訊・OS 組態、免代理程式的遠端查詢 | WMI/CIM | 這正是 WMI 的本領所在。比逐一呼叫各種專用 API 更統一 |
| 讀寫自家應用程式的設定 | 直接讀取登錄檔(Microsoft.Win32.Registry)・組態檔 |
透過 WMI 操作登錄檔繞了遠路,還會背負 7.3 節的位元數問題 |
| CPU 使用率等高頻率・連續性的效能監控 | 效能計數器(System.Diagnostics.PerformanceCounter 等) |
Counter 正是為此而生的機制。WMI 的短週期輪詢在負載與精確度上都不利 |
| 單次的 OS 功能呼叫・需要低延遲的處理 | Win32 API(P/Invoke) | WMI 需要經過 COM/提供者,會有額外的負擔 |
| 以自身處理程序權限即可完成的本機處理程序列舉・操作 | System.Diagnostics.Process |
用標準程式庫即可完成,依賴較少 |
| 防火牆・網路等 Windows 管理功能的組態設定 | Get-NetFirewallRule 等專用的以 CIM 為基礎的 Cmdlet |
比起自行尋找原始 WMI 類別,依用途整理好的 Cmdlet 群組更正確、更安全 |
| 偵測檔案・資料夾的變更 | FileSystemWatcher |
不要在已有專用 API 的領域中引入 WMI |
判斷的軸心很單純,「在已有專用機制的領域使用專用機制,在跨領域查詢・遠端查詢中使用 WMI/CIM」。表格最後一列的 Get-NetFirewallRule 等,內部其實是建構在 CIM 之上的 Cmdlet 群組,可以說是「不直接碰觸 WMI/CIM,只享受其好處」的形式。
9. 總結
- CIM 是 DMTF 的業界標準,WMI 是其 Microsoft 實作。PowerShell 的 CIM Cmdlet 與 C# 的 MI API,都是遵循這套標準的現行世代入口。
- 在 PowerShell 中,Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent 是現行做法。Get-WmiObject 等 WMI Cmdlet 在 PowerShell 7 中並不存在,因此新的指令碼即使目標是 5.1,也應以 CIM 一側撰寫。
- 遠端查詢預設使用 WSMan(WinRM),多次操作要重複使用 CIM 工作階段。對未設定 WinRM 的對象,還有 DCOM 選項這條退路。
- C# 從 System.Management(方便・適合本機)與 Microsoft.Management.Infrastructure(適合遠端・監控,型別體系與 CIM Cmdlet 相同)這兩種體系中選擇。兩者都是僅限 Windows 使用的 NuGet 套件。
- 處理程序監控應使用事件訂閱,而非輪詢。訂閱 Win32_ProcessStartTrace 需要系統管理員權限。
- 避免使用 SELECT * 與短週期輪詢,以 -Filter / -Property / -KeyOnly 進行篩選。請記住:從 32bit 處理程序查詢時會對應到 32bit 提供者、DMTF 日期要使用轉換 API、儲存庫損毀時應依 verify → salvage 的順序處理,而不是直接刪除。
- 在已有專用機制的領域(設定・效能計數器・單次 API 呼叫)不要引入 WMI,跨領域查詢與遠端查詢才是 WMI/CIM 的用武之地 ── 這就是一句話總結的使用時機。
相關文章
- 在 C#(CSharp)中執行 PowerShell 並以物件形式接收結果
- PowerShell 實用指令集錦 ── 累積日常工作常用的小工具
- Windows PowerShell 5.1 與 PowerShell 7 的差異 ── 公司內部腳本遷移實務指南
- 外部設備狀態檢查與顯示的最佳實務 - 別只用『連線中』就交差的設計
- 在 C# 中安全呼叫 Win32 API — P/Invoke 實務指南(DllImport / LibraryImport / CsWin32)
- Windows 的 TPM 是什麼 ── 圖解「不讓金鑰外流的保險箱」與測量啟動
相關諮詢領域
合同會社小村軟體處理將 WMI/CIM 用於硬體資訊取得・處理程序監控・遠端 PC 查詢並整合進業務應用程式、把以 Get-WmiObject 為基礎的公司內部指令碼遷移到 CIM Cmdlet,以及調查「開發機上能動,到客戶端卻出現權限錯誤」這類原因。從 PowerShell 試作到 C# 的正式實作,都可以連貫地與我們洽談。
參考連結
-
Microsoft Learn, About WMI. 關於 WMI 是 WBEM(開發企業環境管理資訊存取標準技術的業界倡議)的 Microsoft 實作,管理對象的表示採用 CIM(Common Information Model)業界標準,CIM 由 DMTF(Distributed Management Task Force)開發・維護,次世代版本 MI(Windows Management Infrastructure)與傳統 WMI 完全相容,遠端 WMI 連線透過 DCOM 進行,並有以 WS-Management 為基礎的 WinRM 作為替代方案等內容。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Differences between Windows PowerShell 5.1 and PowerShell 7.x. 關於 WMI v1 Cmdlet(Register-WmiEvent / Set-WmiInstance / Invoke-WmiMethod / Get-WmiObject / Remove-WmiObject)已從 PowerShell 中移除,CimCmdlets 模組(WMI v2)的 Cmdlet 提供相同功能,並具備新功能與重新設計過的語法等內容。 ↩ ↩2
-
Microsoft Learn, Get-CimInstance (CimCmdlets). 關於未指定 ComputerName 與 CimSession 時會以 COM 工作階段連接本機 WMI,指定 -ComputerName 時會建立 WsMan 通訊協定的臨時工作階段,對同一台電腦進行多次操作時建議以 CIM 工作階段連線以提升效能,-Filter 為不含 WHERE 關鍵字的 WQL/CQL where 子句,-Property 與 -KeyOnly 可用於縮減物件大小與網路流量,預設命名空間為 root/CIMV2、預設查詢語言(-QueryDialect)為 WQL,輸出為 Microsoft.Management.Infrastructure.CimInstance,搭配 Invoke-CimMethod 呼叫 GetOwner 的範例,以及這是僅限 Windows 使用的 Cmdlet 等內容。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6 ↩7 ↩8 ↩9 ↩10
-
Microsoft Learn, New-CimSessionOption (CimCmdlets). 關於 CIM 工作階段選項有 WsMan 用與 DCOM 用兩組參數集,-Protocol 可指定 Dcom / Default / Wsman,以 New-CimSessionOption -Protocol Dcom 建立的選項傳給 New-CimSession 的 -SessionOption 以建立 DCOM 的 CIM 工作階段的範例,以及 DCOM 工作階段預設的模擬層級為 Impersonate 等內容。 ↩ ↩2
-
Microsoft Learn, ManagementObjectSearcher Class (System.Management). 關於這是根據指定的 WQL 查詢取得管理物件集合、取得管理資訊時最常用的入口類別,接收 ObjectQuery 與 ManagementScope(WMI 命名空間)並以 Get() 傳回 ManagementObjectCollection,以及 System.Management.dll 以 NuGet 套件 System.Management 形式提供等內容。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, CimSession Class (Microsoft.Management.Infrastructure). 關於 Microsoft.Management.Infrastructure.dll 以 NuGet 套件 Microsoft.Management.Infrastructure 形式提供,以 Create(computerName) 建立工作階段,以 QueryInstances(namespace, queryDialect, query) 執行查詢,具備 EnumerateInstances / GetInstance / InvokeMethod / Subscribe 及各自的非同步版(*Async),並實作 IDisposable 等內容。 ↩ ↩2 ↩3 ↩4 ↩5
-
Microsoft Learn, Register-CimIndicationEvent (CimCmdlets). 關於以類別名稱或查詢式訂閱指示(事件),用 -SourceIdentifier 為訂閱命名,Win32_ProcessStartTrace 的訂閱範例及其執行需要以系統管理員身分執行 PowerShell 的附註,在 -Action 指令碼區塊中從 $Event.SourceEventArgs.NewEvent 參照 ProcessName / ProcessId 的範例,指定 -ComputerName 時會建立 WsMan 的臨時工作階段、未指定時則以 COM 連接本機,以及使用 Unregister-Event 解除訂閱等內容。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Win32_ProcessStartTrace class. 關於這是表示新處理程序啟動的事件類別,擁有 ProcessName / ProcessID / ParentProcessID / SessionID / Sid 等屬性,SECURITY_DESCRIPTOR 屬性是事件提供者用來決定哪些使用者可以接收事件的描述元,命名空間為 Root\CIMV2,由核心追蹤提供者(Krnlprov.dll)提供等內容。 ↩ ↩2 ↩3
-
Microsoft Learn, Win32_LogicalDisk class. 關於 Win32_LogicalDisk 是繼承自 CIM_LogicalDisk、表示本機儲存裝置的類別,DriveType 的值(2=卸除式、3=本機磁碟、4=網路磁碟機、5=光碟等),FreeSpace / Size 為 uint64 的位元組數值,DeviceID 為主鍵,以及以 DriveType = 3 篩選的 VBScript / C# 查詢範例等內容。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, Installation and configuration for Windows Remote Management. 關於預設情況下未設定 WinRM 接聽器、無法收發 WS-Management 訊息,winrm quickconfig 會執行服務自動啟動化・HTTP/HTTPS 接聽器設定・防火牆例外登錄,WinRM 2.0 的預設連接埠為 HTTP 5985 / HTTPS 5986,在工作群組等無法建立相互驗證(Kerberos)的環境中應盡量限縮設定 TrustedHosts 的範圍,以及控制對接聽器遠端存取的預設安全性描述元(RootSDDL)、允許非系統管理員使用者使用 WMI 外掛時的額外設定等內容。 ↩ ↩2 ↩3 ↩4
-
Microsoft Learn, System.Management Namespace. 關於這是用 ManagementObjectSearcher 系列類別查詢 WMI 基礎架構、用 ManagementEventWatcher 進行事件訂閱的命名空間,WqlEventQuery 表示 WQL 格式的事件查詢,以及 ManagementDateTimeConverter 提供 DMTF 日期時間・時間間隔與 CLR 的 DateTime / TimeSpan 互相轉換的方法等內容。 ↩ ↩2 ↩3
-
Microsoft Learn, Requesting WMI Data on a 64-bit Platform. 關於當提供者同時存在 32bit 版與 64bit 版時,預設會由 32bit 提供者回應 32bit 應用程式(含指令碼)、由 64bit 提供者回應 64bit 應用程式,可透過內容中的 __ProviderArchitecture(32 或 64)與 __RequiredArchitecture 要求・強制使用非預設一側的提供者(強制時若無對應版本則傳回 WBEM_E_PROVIDER_LOAD_FAILURE),以及登錄檔提供者的範例中 32bit 用戶端會收到 HKLM\SOFTWARE\Wow6432Node 一側資料等內容。 ↩ ↩2 ↩3
-
Microsoft Learn, winmgmt. 關於 winmgmt.exe 的 /verifyrepository 會對 WMI 儲存庫進行完整性檢查,/salvagerepository 會在完整性檢查後,若偵測到不一致則重建儲存庫並合併可讀取的內容,/resetrepository 會還原到 OS 初始安裝時的狀態,儲存庫是由 Repository 資料夾內的一群檔案組成資料庫來運作,以及透過 WMI 出現的錯誤有時源自 OS 的其他部分,把刪除儲存庫當成第一個因應手段可能導致系統或已安裝應用程式受損,因此不應該這麼做等內容。 ↩ ↩2 ↩3
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
多執行緒的實務最佳實踐 .NET篇 ── 在增加執行緒之前先決定的事
針對 .NET/C# 整理能防止「建立執行緒後偶爾當機、卡死」的設計定石。內容涵蓋不自行建立執行緒而改用 Task、減少共享可變狀態、鎖的紀律、以 CancellationToken 設計停止機制,以及 UI 執行緒的處理方式。
業務應用程式的日本年號・國定假日・結算日處理 ── 抗改元設計與 JapaneseCalendar・營業日計算實務
報表要顯示「令和8年」、營業日計算要排除國定假日、20日結算次月底付款──日本業務應用程式特有的日期處理,背後藏著「日後會變動的規格」:改元、國定假日的法規修訂、月底的進位。本文整理以 JapaneseCalendar 顯示年號、抗改元的設計方式,國定假日不可寫死在程式碼中...
防止 Windows 應用程式重複啟動 ── 具名 Mutex 與重複執行時的視窗前置
整理業務用 Windows 應用程式的常見需求「不讓同一個應用程式啟動兩次」,如何用具名 Mutex 實作。涵蓋 Global\ 與 Local\ 命名空間差異在 RDP 環境中的陷阱、擁有執行緒限制與 AbandonedMutexException、考量 SetForeg...
在 C#(CSharp)中執行 PowerShell 並以物件形式接收結果
本文從實務角度整理如何從 C# 啟動 PowerShell,並以 PSObject 而非字串來接收結果,涵蓋 PowerShell SDK、AddCommand、AddParameter、BaseObject、Properties 到錯誤處理。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
常見問題
整理諮詢這個主題時常見的問題。
- WMI 和 CIM 有什麼不同?
- CIM 是由 DMTF(Distributed Management Task Force)所制定・維護的「用來表示系統、裝置等管理對象的業界標準模型」。WMI 則是使用該標準的 WBEM 這項倡議的 Microsoft 實作,內建於 Windows 之中。也就是說,CIM 是規格,WMI 是 Windows 上的實作,兩者是這樣的關係。PowerShell 的 Get-CimInstance 或 C# 的 Microsoft.Management.Infrastructure 之所以稱為「CIM」,是因為它們是遵循這套標準的 API,連接的對象仍是同一個 WMI 基礎架構。日常開發中,只要理解成「用 CIM 系列的 API 查詢 WMI 的類別(Win32_* 等)」,實務上就不會有問題。
- Get-WmiObject 已經不能用了嗎?
- 在 Windows PowerShell 5.1 中現在仍可運作,但在 PowerShell 6 以後(現行的 PowerShell 7)中,Get-WmiObject、Invoke-WmiMethod、Register-WmiEvent、Set-WmiInstance、Remove-WmiObject 這些 WMI v1 Cmdlet 已被移除,無法執行。相同的功能由 CimCmdlets 模組(Get-CimInstance / Invoke-CimMethod / Register-CimIndicationEvent 等)提供。新寫的指令碼,即使要在 5.1 上執行,用 CIM Cmdlet 撰寫也比較安全。這樣一來,遷移到 PowerShell 7 時就不需要重寫 WMI 的部分。
- 從 C# 使用 WMI 時,應該用 System.Management 還是 Microsoft.Management.Infrastructure?
- 兩者都僅限 Windows 使用,在現行的 .NET 中都是以 NuGet 套件的形式引用。System.Management 是把 WQL 傳給 ManagementObjectSearcher 就能使用的經典 API,若主要是取得本機資訊,這樣就已足夠。負責轉換 DMTF 日期的 ManagementDateTimeConverter 也包含在其中。另一方面,Microsoft.Management.Infrastructure(MI API)擁有與 PowerShell 的 CIM Cmdlet 相同的型別體系(CimSession / CimInstance),能一貫地處理透過 WSMan 的遠端查詢、非同步版方法,甚至事件訂閱(Subscribe)。若要正式導入遠端 PC 的查詢或監控,選擇 MI API 較為合理。
- 無法從遠端 PC 連上 Get-CimInstance,該檢查什麼?
- 首先請確認連接目標是否已設定 WinRM。指定 -ComputerName 的 CIM 操作,是透過 WSMan(WinRM)通訊協定建立臨時工作階段,因此前提是連接目標的 WinRM 服務與接聽器(listener)正在運作。可以用 winrm quickconfig 進行預設設定(啟動服務・建立接聽器・登錄防火牆例外)。預設連接埠 HTTP 為 5985、HTTPS 為 5986,因此也要確認路徑上的防火牆。在工作群組環境中,因為無法使用 Kerberos 相互驗證,有時需要在用戶端的 TrustedHosts 中登錄。若對方無論如何都無法設定 WinRM,還有一個辦法,就是使用 New-CimSessionOption -Protocol Dcom 建立的選項,改用 DCOM 連線。
- 為什麼 WMI 的日期會以「20260801100000.000000+540」這樣的格式傳回?
- 這是因為 WMI 的日期是以 DMTF 的 CIM 規格所定義的字串格式(yyyymmddHHMMSS.mmmmmm±UUU,結尾為相對 UTC 的偏移分鐘數)儲存的。若用舊版 Get-WmiObject 或 System.Management 讀取原始值,就會直接傳回這個字串。在 C#(System.Management)中,ManagementDateTimeConverter 提供了 DMTF 格式與 DateTime / TimeSpan 互相轉換的方法,請使用這個方法,不要自行對字串進行拆解拼接。另外,若是用 Get-CimInstance 等 CIM 系列 API 取得,日期屬性會以已轉換成 DateTime 的形式傳回,因此根本不會遇到這個問題。