最可靠方法是用管理员权限查询 Win32_PnPSignedDriver,它仅返回已安装且签名有效的PnP驱动,排除残留、禁用设备和未签名驱动;Name常为空,应优先读DeviceName和InfName,DriverDate需正确解析为DateTime。

用 Win32_PnPSignedDriver 查询已安装驱动(最可靠)
Windows 系统里真正“已安装且签名有效”的驱动,Win32_PnPSignedDriver 是 WMI 中最贴近真实状态的类。它不包含残留注册表项、不显示禁用设备的驱动,也不返回未通过签名验证的测试驱动——这正是多数人想查的“实际在用驱动”。
常见错误是直接查 Win32_Driver 或 Win32_SystemDriver:前者字段残缺、后者混入内核服务(如 vmicheartbeat 这种根本不是硬件驱动),查出来一堆干扰项。
- 必须用管理员权限运行,否则部分驱动(尤其是存储、网络类)会缺失或报
Access denied -
Name字段常为空,优先看DeviceName和InfName;DriverDate是DateTime类型,别直接 ToString() - 查询耗时约 200–800ms,避免在 UI 线程频繁调用;建议加
CancellationToken防卡死
var scope = new ManagementScope("\\.\ROOT\CIMV2");
var query = new ObjectQuery("SELECT DeviceName, InfName, DriverDate, DriverVersion FROM Win32_PnPSignedDriver");
using var searcher = new ManagementObjectSearcher(scope, query);
foreach (ManagementObject mo in searcher.Get()) {
Console.WriteLine($"{mo["DeviceName"]} | {mo["InfName"]} | {mo["DriverVersion"]}");
}读取 HKLM\SYSTEM\CurrentControlSet\Control\Class 注册表(需手动过滤)
这个路径下每个子键代表一类设备(比如 {4d36e968-e325-11ce-bfc1-08002be10318} 是磁盘控制器),里面 Driver 值指向实际驱动文件路径。但它的问题很实在:存在大量历史残留、禁用设备、甚至无效键值。
典型错误现象:查出 200+ 条结果,但其中 1/3 的 Driver 值为空,1/4 指向已删除的 .sys 文件,还有些是蓝牙/WiFi 的多版本共存条目(同一设备多个 INF 安装记录)。
- 必须检查
Enum子键是否存在且非空,否则跳过该类设备 - 用
File.Exists((string)regKey.GetValue("Driver"))验证驱动文件是否真实存在 - 注意 32/64 位注册表重定向:x64 进程读
HKLM\SYSTEM\...默认走原生视图,但若目标机器是 WoW64,可能漏掉 32 位驱动(极少见,但某些打印机驱动会出现)
SetupDiGetClassDevs + SetupDiEnumDeviceInfo 获取设备级驱动绑定
这是 SetupAPI 的方式,返回的是“设备实例”和它当前使用的驱动,比 WMI 更底层、也更准——比如热插拔 USB 设备刚连上时,WMI 可能延迟几秒才更新,而 SetupAPI 能立刻反映。
但坑在于:它不返回驱动文件路径或版本号,只给 DriverDate 和 DriverVersion 的字符串(格式不统一,有的带四位点分,有的只有三位),而且必须自己拼出 INF 路径(从 DrvDesc 和 InfPath 推导)。
- 调用前务必用
Guid.Parse("{4d36e968-e325-11ce-bfc1-08002be10318}")明确指定设备类,不能传GUID_NULL查全部,否则性能崩盘 -
SetupDiGetDeviceRegistryProperty读SPDRP_DRIVER才能得到驱动服务名,再查HKLM\SYSTEM\CurrentControlSet\Services\{serviceName}下的ImagePath才是真正的 sys 路径 - 返回的
DriverDate是 FILETIME,得用DateTime.FromFileTimeUtc()转,别用DateTime.FromBinary()
为什么不用 DriverQuery.exe 或 pnputil 就地调用?
命令行工具看着省事,但实际集成进 C# 项目里反而更麻烦:要启动进程、捕获 stdout、解析文本输出(格式随系统语言变化,中文 Win10 的“驱动程序名称”列名就不是英文)、还得处理编码(driverquery /v 默认 GBK,而 pnputil /enum-drivers 是 UTF-16)。一旦用户改了系统区域设置,解析逻辑就挂。
更关键的是,它们返回的信息粒度太粗:driverquery 不区分 PnP 设备和传统 ISA 设备,pnputil 只列 INF 包,不告诉你哪个 INF 正在被哪个设备使用。
- 如果只是临时调试,用
driverquery /v > drivers.txt快速扫一眼没问题 - 但写进正式代码里,必须回归 WMI 或 SetupAPI,二者选其一即可,别混合
- 注意 .NET 6+ 的
System.Management需要额外 NuGet 包System.Management(不是内置的),别漏引用
驱动信息不是静态快照,而是随设备启用/禁用、驱动更新、热插拔实时变化的。哪怕你刚查完列表,下一毫秒就可能有新驱动加载——所以别缓存太久,也别假设“查一次够用”。











