在 C# / PowerShell 中使用 WMI/CIM ── 硬件信息获取・进程监控・远程查询实务指南
· Go Komura · Windows, C#, .NET, PowerShell, WMI, CIM, 业务应用, Windows 开发
“想在业务应用的画面上显示电脑的序列号和机型名称”“想监控服务器的磁盘剩余空间并发出警告”“想检测某个特定进程的启动”“想统一查询远程 PC 的状态”── 在 Windows 业务应用和管理工具的开发中,这类需求是家常便饭。而这类需求的经典答案,就是 WMI(Windows Management Instrumentation),若用标准名称来说则是 CIM(Common Information Model)。
麻烦的是,WMI 相关的信息新旧混杂。搜索时会同时出现使用 Get-WmiObject 的十年前文章和使用 Get-CimInstance 的文章,C# 一侧也存在 System.Management 和 Microsoft.Management.Infrastructure 两大体系。哪种是当前的写法、哪种是『现在仍能运行但不应在新代码中选用』的写法,很难分辨清楚。实际上,Get-WmiObject 在 PowerShell 7 中并不存在,这个问题会在为 5.1 编写的公司内部脚本的迁移过程中突然浮出水面。
本文面向需要在业务应用中实现硬件信息获取、进程监控、远程 PC 查询的 C#/PowerShell 开发者,基于截至 2026 年 8 月的一手资料,梳理从 WMI/CIM 结构的最低限度理解,到 PowerShell 的 CIM cmdlet、C# 的两种 API、常用的实例范例、性能・权限・64 位方面的陷阱,直至『不应使用 WMI 的场景』的判断标准。
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 标准来描述管理对象,内置于 Windows 中1 |
| MI(Windows Management Infrastructure) | WMI 的下一代版本。与传统 WMI 完全兼容,许多新的提供程序都是用 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
# 只把 WHERE 子句写入 -Filter(不写 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)、CD(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. 常见陷阱 ── 性能・权限・64 位・存储库・日期
7.1. SELECT * 与滥用轮询
WMI 的查询是一种『提供程序当场生成值』的处理,并不是免费的。常见的反模式有两种。
- 惰性使用
SELECT *。如果用全部属性获取Win32_Process的全部记录,提供程序的工作量和网络传输量(远程时)都会相应膨胀。用-Filter缩小行,用-Property缩小列,如果后续操作只需要键,则使用-KeyOnly。这些都是官方为『减少对象大小和网络流量』而准备的手段。3 - 短周期轮询。类似『每隔 1 秒执行一次
Get-CimInstance Win32_Process』这样的设计,应替换为 6.3 节介绍的事件订阅。即使无论如何都要使用轮询型(WITHIN),也应将间隔放宽到相对于需求而言必要且充分的程度。
此外,对远程设备逐台使用 -ComputerName 重复调用也是浪费很大的做法。因为每次查询都会执行临时会话的创建,多次操作应改为重复使用 CIM 会话。3
7.2. 事件订阅的权限
如 6.3 节所述,Win32_ProcessStartTrace 系列的订阅以管理员权限为前提。7 『在开发机(以管理员身份运行)上能正常运行,但在客户那边的普通用户环境中监控却不生效』这类事故,与防火墙的通知对话框问题一样常见。如果要在以普通用户身份运行的业务应用中嵌入监控功能,请考虑把监控部分拆分为 Windows 服务(LocalSystem 等),与应用本体之间通过进程间通信连接的方案。
7.3. 32 位/64 位与提供程序
在 64 位 Windows 中,有些提供程序会同时存在 32 位版和 64 位版,默认情况下会由与调用方应用位数一致的一侧做出响应。12 典型的例子是 root\default 的注册表提供程序(StdRegProv):从 32 位应用读取时,返回的是 Wow6432Node 一侧(32 位视图)的值。12 当出现『用 WMI 读到的注册表值与 regedit 中看到的值不一致』时,首先应怀疑这一点。如果需要另一侧的视图,可以在连接时的上下文中指定 __ProviderArchitecture(如果要强制,还可以加上 __RequiredArchitecture)来明确要求。12 关于位数问题的整体情况,也可以参考《在 C# 中安全调用 Win32 API —— P/Invoke 实务指南》。
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 等) |
计数器就是为此而生的机制。WMI 的短周期轮询在负载与精度上都处于劣势 |
| 单次的 OS 功能调用・需要低延迟的处理 | Win32 API(P/Invoke) | WMI 要经过 COM/提供程序,因而带有额外开销 |
| 用自身进程权限即可完成的本地进程枚举・操作 | System.Diagnostics.Process |
用标准库即可完成,减少依赖 |
| 防火墙・网络等 Windows 管理功能的配置 | Get-NetFirewallRule 等专用的基于 CIM 的 cmdlet |
比起寻找原始 WMI 类,按用途整理好的 cmdlet 群更准确、更安全 |
| 文件・文件夹的变更检测 | FileSystemWatcher |
不要把 WMI 引入已有专用 API 的领域 |
判断的准则很简单,『在有专用机制的领域使用专用机制,在跨领域的查询・远程查询中使用 WMI/CIM』。表格最后一行的 Get-NetFirewallRule 等 cmdlet,在内部实际上是构建在 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 加以缩小。请记住:从 32 位进程发起的查询会指向 32 位提供程序;DMTF 日期要使用转换 API;存储库损坏时应按 verify → salvage 的顺序处理,而不是直接删除。
- 不要把 WMI 带入已有专用机制的领域(配置・性能计数器・单次 API 调用),而是把 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=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。关于当提供程序同时存在 32 位版与 64 位版时,默认情况下 32 位应用(包括脚本)会得到 32 位提供程序的响应、64 位应用会得到 64 位提供程序的响应、可以通过上下文中的 __ProviderArchitecture(32 或 64)与 __RequiredArchitecture 请求・强制使用非默认一侧的提供程序(强制时若对应版本不存在则返回 WBEM_E_PROVIDER_LOAD_FAILURE)、以及以注册表提供程序为例说明 32 位客户端会接收到 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 线程的处理方式。
防止 Windows 应用程序重复启动 ── 命名 Mutex 与重复执行时的窗口前置
整理业务型 Windows 应用程序的常见需求——「不让同一个应用程序启动两次」——如何用命名 Mutex 来实现。涵盖 Global\ 与 Local\ 命名空间差异在 RDP 环境中的陷阱、拥有线程限制与 AbandonedMutexException、在 SetFor...
在 C#(CSharp)中执行 PowerShell 并以对象形式接收结果
本文从实务角度整理如何从 C# 启动 PowerShell,并以 PSObject 而非字符串来接收结果,涵盖 PowerShell SDK、AddCommand、AddParameter、BaseObject、Properties 直至错误处理。
Windows证书存储实务指南 ── 应该放入用户存储还是计算机存储
客户端证书究竟应该放入用户存储还是计算机存储?本文从 certmgr.msc 与 certlm.msc 的区别、私钥的权限授予,到 PowerShell 的到期日盘点,系统性地梳理证书相关的常见事故与对策,是一份实务指南。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
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 是只需向 ManagementObjectSearcher 传入 WQL 即可使用的经典 API,如果主要用于本地信息获取,这已经足够。用于转换 DMTF 日期的 ManagementDateTimeConverter 也包含在其中。而 Microsoft.Management.Infrastructure(MI API)拥有与 PowerShell 的 CIM cmdlet 相同的类型体系(CimSession / CimInstance),能够统一处理经由 WSMan 的远程查询、异步版本的方法,乃至事件订阅(Subscribe)。如果要正式地把远程 PC 的查询或监控纳入实现,选择 MI API 更为合理。
- 无法用 Get-CimInstance 连接到远程 PC,应该检查哪些内容?
- 首先请确认目标机器上是否已经配置了 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 形式返回,因此根本不会遇到这个问题。