在 C# 與 PowerShell 中使用 WMI/CIM ── 硬體資訊取得、處理程序監控、遠端查詢實務指南
· Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, 業務應用程式, Windows 開發
更新紀錄(僅初版,2026年08月01日 發布)
- 初次發布
引用本文(DOI(已登錄存檔): 10.5281/zenodo.22175775)
以下 DOI 指向先前登錄的存檔,內容可能與目前正文不同。引用目前正文時,請使用本頁網址。
Go Komura(2026)。〈在 C# 與 PowerShell 中使用 WMI/CIM ── 硬體資訊取得、處理程序監控、遠端查詢實務指南〉。小村軟體有限公司。 https://comcomponent.com/zh-TW/blog/wmi-cim-practical-guide/
- DOI(已登錄存檔)
- 10.5281/zenodo.22175775
- DOI(上次登錄版本)
- 10.5281/zenodo.22175776
「想顯示 PC 的序號與機型名稱」「想查看伺服器的磁碟可用空間」「想偵測處理程序的啟動」。在 Windows 的業務應用程式與管理工具中,能用同一套方法處理這類資訊的就是 WMI/CIM。
CIM 是描述管理資訊的標準模型,WMI 則是採用該標準的 Windows 管理基礎架構。PowerShell 的 CIM Cmdlet 與 C# 的 API,就是存取這套基礎架構的入口。1
flowchart TB
accTitle: 常見需求與 WMI/CIM
accDescr: 顯示序號與機型名稱、監控磁碟可用空間、偵測處理程序啟動、查詢遠端 PC,這些業務應用程式常見需求的標準答案都是 WMI,而管理資訊的表示則採用 CIM 標準
r1["序號與機型名稱"] --> ans["WMI(採用 CIM 標準的基礎架構)"]
r2["磁碟可用空間的監控"] --> ans
r3["處理程序啟動的偵測"] --> ans
r4["遠端 PC 的查詢"] --> ans
圖 1: 業務應用程式最常見的四類需求,標準答案都是 WMI/CIM。
容易讓人猶豫的是入口不只一個。搜尋結果中舊版 Get-WmiObject 與 Get-CimInstance 混雜,C# 這邊也有 System.Management 與 Microsoft.Management.Infrastructure。舊版 WMI Cmdlet 在 Windows PowerShell 5.1 中還能用,但 PowerShell 7 裡已經沒有了。2
本文寫給要實作硬體資訊取得、處理程序監控、遠端 PC 查詢的 C#/PowerShell 開發者。內容依照先判斷該不該選 WMI,接著在 PowerShell 上試,確認連線條件後做進 C#,最後把監控與故障處理也一併備齊的順序說明。全文以 2026 年 8 月時點的第一手資料為依據,整理實際範例與注意事項。
1. 先講結論:依用途與連線對象挑選入口
新寫的 PowerShell 一律使用 CIM Cmdlet,C# 則依用途挑選 API。不過,凡是專用機制就能解決的處理,沒有必要硬往 WMI 靠。
| 判斷與作業 | 基本方針 | 說明的章節 |
|---|---|---|
| 決定該不該用 WMI | 用於跨領域的資訊取得與遠端查詢,設定與高頻率的效能監控則選專用機制 | 第 2 章 |
| 決定要查詢什麼 | 挑選命名空間、類別與屬性,再用 WQL 縮小對象。日常的預設是 root/CIMV23 |
第 3、4 章 |
| 在 PowerShell 上試 | 用 Get-CimInstance 讀取,用 Invoke-CimMethod 操作。就算是給 5.1 用的新程式碼也寫在 CIM 這一側2 |
第 4 章 |
| 擴展到遠端 | 以 WSMan/WinRM 為基本,對同一對象的多次操作則重複使用 CIM 工作階段。必要時選擇 DCOM34 | 第 5 章 |
| 做進 C# | 本機的輕量查詢用 System.Management,要把遠端或監控做進產品則考慮 MI API56 |
第 6 章 |
| 監控處理程序啟動 | 使用事件訂閱,並把權限、取消訂閱、中斷後的重新註冊一併納入設計78 | 第 7 章 |
| 調查慢、值不對、找不到類別的原因 | 從查詢的量與頻率、提供者的位元數、儲存庫的一致性逐一釐清 | 第 8 章 |
特別要注意的是,能取得清單和能訂閱事件是兩回事。Win32_ProcessStartTrace 的訂閱範例要以系統管理員權限執行。7 另外,不要惰性地使用 SELECT * 或短週期輪詢,而要用 -Filter、-Property、-KeyOnly 把範圍縮到需要的資訊。3
圖中實線表示始終成立的關係,虛線表示附帶條件的關係(成立條件寫在詳細頁面中各關係的說明中)。關係的完整清單(共 26 條,附依據與可信度)以及主要概念的定義,彙整在知識地圖詳細頁面(日文)。資料:JSON-LD / Turtle
2. 什麼時候用 WMI/CIM,什麼時候選專用機制
WMI 作為「統一的讀取入口」相當出色,但並非永遠是最佳解。以下是實務上區分使用的大致參考。
| 想做的事 | 合適的手段 | 不選 WMI 的理由 |
|---|---|---|
| 取得硬體資訊與 OS 組態、無代理程式的遠端查詢 | WMI/CIM | 這正是 WMI 的本行。比起逐一呼叫專用 API 更為統一 |
| 自家應用程式的設定讀寫 | 直接讀取登錄檔(Microsoft.Win32.Registry)或設定檔 |
透過 WMI 操作登錄檔繞了遠路,還會一併扛下 8.2 節的位元數問題 |
| CPU 使用率等高頻率、連續性的效能監控 | 效能計數器(System.Diagnostics.PerformanceCounter 等) |
計數器本來就是為此而設。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,只享受它帶來的好處」的形式。
flowchart TB
accTitle: 手段的判斷準則
accDescr: 判斷準則是有專用機制的領域就用專用機制,沒有的領域則把跨領域查詢與遠端查詢交給 WMI 與 CIM,而以 CIM 為基礎的專用 Cmdlet 是不直接碰 WMI 與 CIM 只享受好處的形式
q1{"有專用機制嗎?"} -->|有| ded["使用專用機制"]
q1 -->|沒有| wmi["使用 WMI / CIM"]
wmi -.-> use["跨領域查詢與遠端查詢"]
cmd["以 CIM 為基礎的專用 Cmdlet"] -.-> ben["只享受好處的形式"]
圖 2: 有專用機制的領域就用專用機制,跨領域查詢與遠端查詢才用 WMI/CIM。
3. 掌握運作機制:標準、命名空間、類別、WQL
3.1. CIM 是標準,WMI 是 Windows 上的實作
先把術語之間的關係一次整理清楚。
| 術語 | 實際是什麼 |
|---|---|
| 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 完全相容,許多新的提供者都以 MI 撰寫1 |
flowchart TB
accTitle: CIM 標準與 WMI 實作的關係
accDescr: 由 DMTF 制定並維護的 CIM 標準在 WBEM 倡議的框架下被使用,其 Microsoft 實作就是 WMI,次世代版本的 MI 與傳統 WMI 完全相容,而 CIM 系列 API 連接的對象都是同一套 WMI 基礎架構
dmtf["由 DMTF 制定並維護"] --> cim["CIM(業界標準模型)"]
wbem["WBEM(業界倡議)"] --> wmi["WMI(Microsoft 實作)"]
cim --> wmi
wmi -.-> mi["MI(次世代版本,完全相容)"]
api["CIM 系列 API(PowerShell / C#)"] --> wmi
圖 3: CIM 是規格,WMI 是 Windows 上的實作。CIM 系列 API 連接的對象都是同一套 WMI 基礎架構。
3.2. 把命名空間、類別、提供者、WQL 串起來理解
身為開發者要掌握的結構有以下四項。
- 命名空間(namespace):把類別集合起來的階層。日常查詢用到的幾乎都是 root/CIMV2,CIM Cmdlet 的預設值也是這裡。3 此外還有
root\default(登錄檔提供者等)等命名空間。 - 類別:像
Win32_ComputerSystem(電腦本體)、Win32_LogicalDisk(邏輯磁碟機)、Win32_Process(處理程序)這樣的管理對象型別。繼承自 CIM 標準類別(如CIM_LogicalDisk)的 Windows 專屬類別,會帶有Win32_前綴。9 - 提供者:提供類別實體的元件。一旦查詢,提供者就會當場向 OS 詢問並產生值。
- WQL:類似 SQL 的查詢語言。像
SELECT Name, State FROM Win32_Service WHERE StartMode = 'Auto'這樣,把類別看成資料表來篩選。CIM Cmdlet 的預設查詢語言也是 WQL。3
「能以統一的類別與查詢語言讀取 OS 與硬體的資訊」── 這就是 WMI 的價值所在。不過,並不是每個類別都能寫入或操作。有方法的類別用 Invoke-CimMethod 操作,可寫入屬性的變更則用 Set-CimInstance。就像 4.1 節的對照表那樣,要依類別提供的功能挑選入口。
flowchart TB
accTitle: WMI 查詢的結構
accDescr: WQL 的查詢指向命名空間 root/CIMV2 中的 Win32_* 類別,提供類別實體的提供者當場向 OS 詢問並產生值,然後傳回結果
wql["以 WQL 查詢"] --> ns["命名空間 root/CIMV2"]
ns --> cls["Win32_* 類別"]
cls --> prov["提供者"]
prov --> osq["當場向 OS 詢問"]
osq --> res["傳回結果"]
圖 4: 查詢依命名空間、類別、提供者的順序往下走,值是當場產生的。
4. 在 PowerShell 上試:讀取、方法呼叫、資產資訊
接下來在 Windows 上的 PowerShell 實際操作。就算是要從舊程式碼搬過來,也請先確認 Cmdlet 的對應關係,以及傳回值處理方式的差異。
4.1. 新程式碼一律用 CIM 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 |
事件訂閱(第 7 章) |
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 的差異」一文中。
flowchart TB
accTitle: 新指令碼要用 CIM 撰寫的理由
accDescr: 用 WMI Cmdlet 寫的指令碼雖然能在 5.1 上執行,但因為在 PowerShell 6 以後已遭移除,遷移時必須重寫;CIM Cmdlet 在 5.1 上也能使用,所以新寫的東西只要寫在 CIM 這一側就不會留下遷移成本
new["新寫的指令碼"] --> q1{"要用哪一邊寫?"}
q1 -->|WMI Cmdlet| old["在 5.1 上能執行"]
q1 -->|CIM Cmdlet| cur["在 5.1 上也能使用"]
old --> del["PowerShell 7 中已移除"]
del --> rew["遷移時要重寫"]
cur --> norew["不會留下遷移成本"]
圖 5: 新的東西只要先用 CIM Cmdlet 寫好,遷移到 PowerShell 7 時就不必重寫。
4.2. 用 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 的形式傳回。
4.3. 方法要用 Invoke-CimMethod 呼叫
與舊版 Get-WmiObject 不同,取得的物件並不是拿來直接呼叫 WMI 方法的,因此方法呼叫要交給 Invoke-CimMethod。
flowchart TB
accTitle: CimInstance 的方法呼叫
accDescr: Get-CimInstance 傳回的 CimInstance 一方面日期屬性已轉換成 DateTime,另一方面卻不是用來直接呼叫 WMI 方法的,因此方法呼叫要把執行個體傳給 Invoke-CimMethod 來進行
gci["Get-CimInstance"] --> inst["CimInstance 物件"]
inst -.-> dt["日期已轉換成 DateTime"]
inst -.-> nom["WMI 方法走另一個入口"]
inst --> icm["傳給 Invoke-CimMethod"]
icm --> call["方法呼叫"]
圖 6: CimInstance 的 WMI 方法不直接呼叫,方法呼叫要把執行個體傳給 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
4.4. 依想取得的資訊挑選類別
| 想取得的資訊 | 類別 | 主要屬性 |
|---|---|---|
| 製造商與機型名稱 | 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 |
4.5. 資產管理範例:機型、序號、磁碟可用空間
# 機型資訊與序號(用於比對 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)與 CD(5)。9 在滿足第 5 章的連線條件之後,把這段指令碼透過 CIM 工作階段送到各台伺服器執行,就能打好無代理程式磁碟監控的基礎。
flowchart TB
accTitle: 無代理程式磁碟監控的基礎
accDescr: 用 DriveType 3 篩選以排除卸除式磁碟、網路磁碟機與 CD,只鎖定本機磁碟,再把同一段指令碼透過 CIM 工作階段送到各台伺服器執行,就能打好無代理程式磁碟監控的基礎
scr["取得可用空間的指令碼"] --> flt["以 DriveType = 3 篩選"]
flt -.-> exc["排除卸除式磁碟等"]
scr --> ses["透過 CIM 工作階段"]
ses --> srvs["送到各台伺服器"]
srvs --> mon["無代理程式監控"]
圖 7: 把限定在本機磁碟的指令碼透過 CIM 工作階段送到各台伺服器,就是監控的基礎。
5. 擴展到遠端:連線方式與前提條件
5.1. 本機與遠端的連線方式並不相同
沒有傳入 -CimSession 的 CIM Cmdlet,若連 -ComputerName 也沒指定,就會以 COM 連接本機的 WMI;一旦指定 -ComputerName,則會以 WSMan(WinRM)通訊協定建立臨時工作階段再連線。對同一台電腦要進行多次操作時,建立 CIM 工作階段重複使用在效能上比較有利。3
flowchart TB
accTitle: CIM 連線方式的選擇
accDescr: 未傳入 CimSession 時,沒有指定 ComputerName 就以 COM 連接本機 WMI,指定 ComputerName 則每次查詢都會建立 WSMan 的臨時工作階段;對同一對象的多次操作,重複使用 New-CimSession 在效能上比較有利,而對未設定 WinRM 的對象則有 DCOM 通訊協定選項
exec["不傳入 CimSession 執行"] --> q1{"有指定 ComputerName 嗎?"}
q1 -->|沒有| local["以 COM 連接本機 WMI"]
q1 -->|有| q2{"對同一對象多次操作?"}
q2 -->|單次| temp["WSMan 的臨時工作階段"]
q2 -->|多次| sess["重複使用 New-CimSession"]
temp -.-> cost["每次查詢都會建立"]
nowinrm["未設定 WinRM 的對象"] -.-> dcom["DCOM 通訊協定選項"]
圖 8: 遠端查詢預設使用 WSMan,對同一對象的多次操作則以重複使用 CIM 工作階段為標準做法。
5.2. 備妥 WinRM、防火牆、驗證與權限
以 WSMan/WinRM 查詢時的前提如下。在寫連線處理之前,請把服務、通訊路徑、驗證、權限分開逐一確認。
- 連線目標已設定好 WinRM。
winrm quickconfig會一次完成把服務改為自動啟動、建立 HTTP 接聽器(預設連接埠 5985)、登錄防火牆例外。10 若要以 HTTPS(預設連接埠 5986)連線,光靠這樣還不夠,必須先備妥伺服器憑證,再用winrm quickconfig -transport:https之類的方式另外設定 HTTPS 接聽器。10 - 路徑上的防火牆已開放對應的連接埠。輸入規則的設計與登錄實務,就如「Windows 防火牆與業務應用程式」一文所述。
- 驗證。在網域環境中會以 Kerberos 進行相互驗證。工作群組環境無法使用 Kerberos,因此有時必須在用戶端的
TrustedHosts中登錄。登錄的對象要縮到最小必要範圍。10 - 權限。在預設設定下,遠端的 WMI 查詢與操作基本上要用隸屬於連線目標系統管理員群組的帳戶執行。若要開放給一般使用者,則 WinRM 與 WMI 命名空間兩邊都需要設定存取權限。10
flowchart TB
accTitle: 遠端查詢前提條件的確認
accDescr: 在連線目標上 winrm quickconfig 會一次完成把服務改為自動啟動、建立 HTTP 接聽器與登錄防火牆例外,HTTPS 接聽器則要備妥憑證另外設定,而在工作群組環境中有時必須登錄 TrustedHosts
qc["winrm quickconfig"] --> svc["把服務改為自動啟動"]
qc --> lis["建立 HTTP 接聽器(5985)"]
qc --> fw["防火牆例外"]
lis ~~~ https["HTTPS 接聽器(5986)"]
https -.-> cert["備妥憑證另外設定"]
fw ~~~ wg["工作群組環境"]
wg -.-> th["僅在必要時限定登錄"]
圖 9: winrm quickconfig 會一次完成預設設定,HTTPS 接聽器與工作群組的驗證則要另外處理。
5.3. 對同一對象的多次操作要重複使用 CIM 工作階段
# 單次就用 -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
5.4. 對無法設定 WinRM 的對象可考慮 DCOM
對於無法設定 WinRM 的老舊機器等無法以 WSMan 連線的對象,可以選擇 DCOM 通訊協定。4
$dcom = New-CimSessionOption -Protocol Dcom
$session = New-CimSession -ComputerName OldServer -SessionOption $dcom
DCOM 還會用到 RPC 的動態連接埠,因此要跨越防火牆的設計就變得困難。今後要打造的機制,還是把 WSMan 當成預設比較穩當。
上面的工作階段範例只是用來示範連線方式。實際做進產品時,請用 try / finally 之類的方式確保善後處理,讓查詢中途失敗也仍然會走到 Remove-CimSession。用 DCOM 範例建立的工作階段同樣要釋放。
6. 做進 C#:兩套 API 以及型別與日期的處理
6.1. 依用途在兩套 API 之間選擇
從 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 併用的設計 |
6.2. System.Management:傳入 WQL,依 CIM 型別讀取
以字串形式傳入 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,是最典型的第一個絆腳石。
flowchart TB
accTitle: 屬性取得與轉型的陷阱
accDescr: System.Management 的屬性是透過索引子以 object 形式傳回的,因此必須在類別文件中確認 CIM 型別再轉型,若自以為是 int 就轉型會收到 InvalidCastException
idx["透過索引子取得"] --> obj["以 object 形式傳回"]
obj --> chk["在文件中確認 CIM 型別"]
chk --> cast["轉型為正確的型別"]
obj -.-> wrong["自以為是 int 就轉型"]
wrong -.-> ex["InvalidCastException"]
圖 10: 屬性是以 object 傳回的,要先確認 CIM 型別再轉型。
6.3. MI API:用與 PowerShell 相同的型別查詢
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# 中執行 PowerShell 並以物件形式接收結果的方法」。
flowchart TB
accTitle: 先在 PowerShell 上試再抄寫成 C# 的流程
accDescr: PowerShell 的 CIM Cmdlet 與 C# 的 MI API 處理的是相同的 CimInstance 型別,因此先在 PowerShell 上試作再抄寫成 C# 的開發流程能自然銜接
trial["在 PowerShell 上試作"] --> gci["CIM Cmdlet"]
impl["在 C# 中正式實作"] --> mi["MI API"]
gci --> ci["相同的 CimInstance 型別"]
mi --> ci
ci -.-> flow["抄寫能自然銜接"]
圖 11: CIM Cmdlet 與 MI API 處理的是相同的 CimInstance 型別,因此能從試作接到正式實作。
6.4. DMTF 日期不要手工拆解拼接,交給轉換 API
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用於計算。
flowchart TB
accTitle: DMTF 日期格式的處理
accDescr: WMI 的日期是以 DMTF 格式的字串儲存的,若用 System.Management 讀取原始值就用 ManagementDateTimeConverter 轉換,若是 CIM 系列 API 則會以已轉換的 DateTime 傳回,因此不要自行拆解拼接字串
dmtf["DMTF 格式的字串"] --> q1{"用哪一套 API 取得?"}
q1 -->|System.Management| conv["ManagementDateTimeConverter"]
q1 -->|CIM 系列 API| done["以已轉換的 DateTime 傳回"]
conv --> dtv["轉換為 DateTime / TimeSpan"]
cut["自行拆解拼接字串"] -.-> ng["不要使用"]
圖 12: DMTF 字串的轉換交給轉換 API,若是 CIM 系列 API 就直接使用已轉換好的 DateTime。
這裡的程式碼只是 API 的基本形。實際的應用程式中,除了取得失敗與未設定值的處理之外,還要依所使用的 API 實作結果集合、取得物件與工作階段的釋放。
7. 監控處理程序啟動:權限、訂閱、取消、重新註冊
不要「用輪詢定期取得 Win32_Process 再看差異」,而要改用事件訂閱。處理程序的啟動,訂閱 Win32_ProcessStartTrace(核心追蹤提供者的事件類別,具有 ProcessName / ProcessID / ParentProcessID 等屬性8)最為簡單。
7.1. 先決定執行權限與監控程式碼的落腳處
Register-CimIndicationEvent 以類別名稱或 WQL 事件查詢登錄訂閱,每當事件送達就執行 -Action 的指令碼區塊。7 訂閱這個類別需要系統管理員權限。7 誰能接收事件是由事件類別的安全性描述元控制的,維持一般使用者身分就會被拒絕存取。8
Win32_ProcessStartTrace 這一類的訂閱以系統管理員權限為前提。7「在開發機(以系統管理員身分執行)上能動,到了客戶端的一般使用者環境監控就不動」這種問題,和防火牆的通知對話方塊並列為最常見的狀況。若要在以一般使用者身分執行的業務應用程式中加入監控,請考慮把監控部分拆到 Windows 服務(LocalSystem 等)上,再用處理程序間通訊與應用程式本體連接的架構。
flowchart TB
accTitle: 一般使用者環境下的監控架構
accDescr: 以系統管理員權限為前提的訂閱要從以一般使用者身分執行的應用程式本體切出來,把監控部分拆到以 LocalSystem 等身分執行的 Windows 服務上,再用處理程序間通訊與應用程式本體連接
svcm["監控用的 Windows 服務"] --> subm["訂閱啟動追蹤"]
svcm -.-> lsm["以 LocalSystem 等身分執行"]
appm["應用程式本體(一般使用者)"] ---|處理程序間通訊| svcm
圖 13: 需要系統管理員權限的訂閱拆到服務那一側,再用處理程序間通訊與應用程式本體連接。
7.2. 用 PowerShell 訂閱,並在監控結束時取消
# 請在具系統管理員權限的 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
sequenceDiagram
accTitle: 處理程序啟動事件訂閱的流程
accDescr: 在具系統管理員權限的 PowerShell 工作階段中以 Register-CimIndicationEvent 登錄訂閱,每當有處理程序啟動就送達事件並執行 Action,結束監控時再以 Unregister-Event 取消
participant ps as PowerShell 工作階段
participant wmi as WMI
ps->>wmi: 以 Register-CimIndicationEvent 登錄訂閱
Note over ps: 以系統管理員權限執行
wmi-->>ps: 每當處理程序啟動就送達事件
ps->>ps: 執行 -Action
ps->>wmi: 以 Unregister-Event 取消(監控結束時)
圖 14: 訂閱與登錄它的工作階段綁在一起,取消要在結束監控時進行。
7.3. 使用 WITHIN 的通用事件本質上是輪詢
另一種方法,是監控類別執行個體建立的通用執行個體建立事件(__InstanceCreationEvent)。這一種的運作方式是由 WMI 以 WITHIN 指定的間隔輪詢,再把差異轉成事件,所以偵測間隔與負載之間的取捨得由自己決定。
flowchart TB
accTitle: 偵測處理程序啟動的兩種訂閱方式
accDescr: Win32_ProcessStartTrace 是訂閱核心追蹤提供者事件類別的方式,通用的 __InstanceCreationEvent 則是由 WMI 以 WITHIN 指定的間隔輪詢再把差異轉成事件,因此偵測間隔與負載的取捨要自己決定
goal["處理程序啟動的偵測"] --> t1["Win32_ProcessStartTrace"]
goal --> t2["__InstanceCreationEvent"]
t1 -.-> k1["訂閱核心追蹤"]
t2 -.-> w1["以 WITHIN 間隔輪詢"]
w1 -.-> tr["間隔與負載的取捨"]
圖 15: 是要訂閱專用的事件類別,還是要用附輪詢間隔的通用執行個體建立事件。
# 以 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)"
}
7.4. 在 C# 中用 ManagementEventWatcher 接收
在 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.5. 常駐監控要連訂閱中斷後的重新註冊一併設計
若要做進常駐監控,請把訂閱中斷時(服務重新啟動時、發生錯誤時)的重新註冊也納入設計。包含裝置監控在內的「狀態確認與顯示」設計論,在「外部設備狀態檢查與顯示的最佳實務」一文中有所探討。
stateDiagram-v2
accTitle: 常駐監控的訂閱生命週期
accDescr: 常駐監控中訂閱中的狀態可能因服務重新啟動或發生錯誤而中斷,因此要把偵測到中斷後重新註冊並回到訂閱中的設計也一併納入
s1: 訂閱中
s2: 訂閱已中斷的狀態
s3: 重新註冊
[*] --> s1
s1 --> s2: 服務重新啟動、發生錯誤
s2 --> s3
s3 --> s1
圖 16: 常駐監控要連訂閱中斷時重新註冊、回到訂閱中的設計一併納入。
8. 釐清故障原因:效能、位元數、儲存庫
連不上時回到 5.2 節,只有訂閱被拒絕時回到 7.1 節,轉型與日期的處理則回到 6.2 與 6.4 節確認。這裡要分開處理的,是除此之外常遇到的「慢」「值不對」「找不到類別」。
8.1. 慢:重新檢視取得的量、頻率與工作階段
WMI 的查詢是「提供者當場產生值」的處理,並非不用成本。常見的反模式有兩個。
- 惰性地使用
SELECT *。把Win32_Process的全部屬性、全部筆數都取回來,提供者的工作量與網路傳輸(遠端時)就會等比例膨脹。用-Filter篩選行、用-Property篩選列,若只是為了後續操作而需要索引鍵,就用-KeyOnly。這些都是官方為了「減少物件大小與網路流量」而準備的手段。3 - 短週期輪詢。像「每 1 秒執行一次
Get-CimInstance Win32_Process」這樣的設計,要換成第 7 章的事件訂閱。就算無論如何都得用輪詢型(WITHIN),也要把間隔放寬到對需求而言剛好足夠的程度。
另外,對遠端一台一台用 -ComputerName 反覆執行也是浪費很大的做法。因為每次查詢都會建立臨時工作階段,所以多次操作要改成重複使用 CIM 工作階段。3
flowchart TB
accTitle: 效能反模式與替代做法
accDescr: 惰性使用 SELECT 星號要改成用 Filter 與 Property 篩選行與列,只需要索引鍵時用 KeyOnly,短週期輪詢要改成事件訂閱,一台一台指定 ComputerName 反覆執行要改成重複使用 CIM 工作階段
a1["惰性使用 SELECT *"] --> f1["用 Filter 與 Property 篩選"]
f1 -.-> f2["只需索引鍵時用 KeyOnly"]
a2["短週期輪詢"] --> f3["改成事件訂閱"]
a3["一台一台指定 ComputerName"] --> f4["重複使用 CIM 工作階段"]
圖 17: 篩選行、列與索引鍵,選對監控方式,重複使用工作階段,就能壓下不必要的負載。
8.2. 值不對:確認 32bit/64bit 的提供者
在 64bit Windows 上,有些提供者同時存在 32bit 版與 64bit 版,預設會由與呼叫端應用程式位元數一致的一方回應。12 典型的例子是 root\default 的登錄檔提供者(StdRegProv),從 32bit 應用程式讀取時,傳回的是 Wow6432Node 那一側(32bit 檢視)的值。12 遇到「用 WMI 讀到的登錄檔值和用 regedit 看到的不一樣」時,請先懷疑這一點。
若需要另一側的檢視,可以在連線時的內容中指定 __ProviderArchitecture(若要設為必要條件則再加上 __RequiredArchitecture)來明確要求。12 位元數問題的整體樣貌,在「在 C# 中安全呼叫 Win32 API ── P/Invoke 實務指南」一文中也有探討。
flowchart TB
accTitle: 64bit 環境下的提供者選擇
accDescr: 預設會由與呼叫端應用程式位元數一致的提供者回應,32bit 應用程式的登錄檔查詢會收到 Wow6432Node 那一側的值,但可以用 __ProviderArchitecture 指定明確要求另一側的檢視
q1{"呼叫端是幾位元?"} -->|32bit| p32["由 32bit 提供者回應"]
q1 -->|64bit| p64["由 64bit 提供者回應"]
p32 -.-> wow["登錄檔是 Wow6432Node 那一側的值"]
ctx["指定 __ProviderArchitecture"] -.-> ov["明確要求另一側的檢視"]
圖 18: 預設由與呼叫端位元數一致的一方回應,因此 32bit 應用程式讀到的是 Wow6432Node 那一側。
8.3. 找不到類別:儲存庫要先確認再修復
WMI 的類別定義存放在儲存庫(並非單一檔案,而是由 Repository 資料夾內的一群檔案作為資料庫運作13)之中。一旦它出現不一致,就算應用程式那邊什麼都沒改,也會開始出現「本該存在的類別找不到」「命名空間無效」這類錯誤。
釐清原因與修復都使用 winmgmt.exe。13
rem 一致性檢查(結果若為 inconsistent 就代表有不一致)
winmgmt /verifyrepository
rem 一致性檢查,若有不一致就重建(可讀取的內容會被合併)
winmgmt /salvagerepository
要注意的是,不要把刪除或初始化儲存庫當成第一步。透過 WMI 出現的錯誤有時源自 OS 的其他部分,Microsoft 也明確表示把刪除儲存庫當成第一個處置「可能導致系統或已安裝的應用程式受損」。13 請遵守先用 /verifyrepository 確認、再用 /salvagerepository 修復的順序。
flowchart TB
accTitle: WMI 儲存庫不一致的釐清步驟
accDescr: 出現找不到類別等錯誤時先用 winmgmt 的 verifyrepository 確認一致性,若有不一致就用 salvagerepository 重建,不要把刪除或初始化儲存庫當成第一步
sym["找不到類別等錯誤"] --> verify["winmgmt /verifyrepository"]
verify --> q1{"結果是 inconsistent 嗎?"}
q1 -->|是| salvage["winmgmt /salvagerepository"]
q1 -->|否| other["懷疑是 OS 其他部分的原因"]
salvage -.-> merge["可讀取的內容會被合併"]
del["刪除或初始化儲存庫"] -.-> ng["不要當成第一步"]
圖 19: 遵守先 verify 確認再 salvage 修復的順序,不要把刪除當成第一步。
9. 總結:把查詢、監控與維運串起來
WMI/CIM 是跨領域查詢 Windows 管理資訊的共通入口,並不是連專用 API 就能解決的處理也全都收攏進來的工具。
| 導入階段 | 要確認的事項 |
|---|---|
| 挑選工具 | 設定、高頻率的效能監控、單次的 OS 操作優先使用專用機制,資訊取得與遠端查詢才用 WMI/CIM |
| 在 PowerShell 上試 | 即使是給 5.1 用的也用 CIM Cmdlet 撰寫。挑選 root/CIMV2 的類別,並篩選行、列與索引鍵 |
| 擴展到遠端 | 備妥 WSMan/WinRM 的連線條件,多次操作重複使用工作階段。對必要的對象則考慮 DCOM 選項 |
| 搬到 C# | 在適合本機查詢的 System.Management 與適合遠端、監控的 MI API 之間分別選用。確認型別與日期的處理 |
| 做成常駐監控 | 確認 Win32_ProcessStartTrace 的權限,並把訂閱、取消、中斷後的重新註冊連成一整套設計 |
| 調查故障 | 從短週期輪詢、提供者的位元數、儲存庫的一致性逐一釐清。不要把刪除當成第一個處置 |
把 CIM 這個標準與 WMI 這個實作、查詢與事件訂閱、連線與存取權限分開來思考,該選哪個工具、該確認哪裡就會變得清楚。請先用 PowerShell 取得需要的資訊,再把結果接到 C# 的實作與維運設計上。
相關文章
- 在 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 ↩3
-
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 的範例,以及本 Cmdlet 僅限 Windows 的說明。 ↩ ↩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 ↩5
-
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=CD 等);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 會還原成作業系統初次安裝時的狀態;儲存庫是由 Repository 資料夾內的一群檔案作為資料庫運作;並說明透過 WMI 出現的錯誤有時源自作業系統的其他部分,因此不應把刪除儲存庫當成第一個處置,否則可能導致系統或已安裝的應用程式受損。 ↩ ↩2 ↩3
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
委外・委託開發 Windows 應用程式前該整理的事項
在委外・委託開發 Windows 應用程式之前,整理既有軟體改版、設備整合、COM/ActiveX、發布與更新、維護等應留意的重點。
引數為什麼會壞掉 ── Windows 命令列引數的規則
在 Windows 上並不存在引數的陣列,傳給 CreateProcess 的是一條字串,分割由接收端負責。本文說明 CommandLineToArgvW・CRT・.NET 的分割規則,以及 .NET 的 ArgumentList 與 C++ 中正確的組裝方式。
多執行緒的實務最佳實踐 .NET篇 ── 在增加執行緒之前先決定的事
針對 .NET/C# 整理能防止「建立執行緒後偶爾當機、卡死」的設計定石。內容涵蓋不自行建立執行緒而改用 Task、減少共享可變狀態、鎖的紀律、以 CancellationToken 設計停止機制,以及 UI 執行緒的處理方式。
業務應用程式的日本年號・國定假日・結算日處理 ── 抗改元設計與 JapaneseCalendar・營業日計算實務
報表要顯示「令和8年」、營業日計算要排除國定假日、20日結算次月底付款──日本業務應用程式特有的日期處理,背後藏著「日後會變動的規格」:改元、國定假日的法規修訂、月底的進位。本文整理以 JapaneseCalendar 顯示年號、抗改元的設計方式,國定假日不可寫死在程式碼中...
防止 Windows 應用程式重複啟動 ── 具名 Mutex 與重複執行時的視窗前置
整理業務用 Windows 應用程式的常見需求「不讓同一個應用程式啟動兩次」,如何用具名 Mutex 實作。涵蓋 Global\ 與 Local\ 命名空間差異在 RDP 環境中的陷阱、擁有執行緒限制與 AbandonedMutexException、考量 SetForeg...
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
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 服務與接聽器正在運作。可以用 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 的形式傳回,因此根本不會遇到這個問題。