获取连接的 USB 设备列表
Posted
技术标签:
【中文标题】获取连接的 USB 设备列表【英文标题】:Get List of connected USB Devices 【发布时间】:2011-03-20 20:25:27 【问题描述】:如何获取 Windows 计算机上所有已连接 USB 设备的列表?
【问题讨论】:
【参考方案1】:为您的项目添加对 System.Management 的引用,然后尝试以下操作:
namespace ConsoleApplication1
using System;
using System.Collections.Generic;
using System.Management; // need to add System.Management to your project references.
class Program
static void Main(string[] args)
var usbDevices = GetUSBDevices();
foreach (var usbDevice in usbDevices)
Console.WriteLine("Device ID: 0, PNP Device ID: 1, Description: 2",
usbDevice.DeviceID, usbDevice.PnpDeviceID, usbDevice.Description);
Console.Read();
static List<USBDeviceInfo> GetUSBDevices()
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_USBHub"))
collection = searcher.Get();
foreach (var device in collection)
devices.Add(new USBDeviceInfo(
(string)device.GetPropertyValue("DeviceID"),
(string)device.GetPropertyValue("PNPDeviceID"),
(string)device.GetPropertyValue("Description")
));
collection.Dispose();
return devices;
class USBDeviceInfo
public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
this.DeviceID = deviceID;
this.PnpDeviceID = pnpDeviceID;
this.Description = description;
public string DeviceID get; private set;
public string PnpDeviceID get; private set;
public string Description get; private set;
【讨论】:
有没有办法检索设备的友好名称?例如,当我进入我的 USB 记忆棒的属性时,我看到“Kingston DataTraveler 2.0 USB 设备”。 DeviceID和PNPDeviceID有什么区别? 当我运行上述程序时,我得到了我的 USB 硬盘、我的键盘和鼠标,但我没有得到我的 USB 摄像头、我的 USB A/D。为什么我的所有 USB 设备都没有显示? 应该查询“Win32_USBControllerDevice”而不是“Win32_USBHub”以便接收所有USB设备的列表。然后使用“Dependent”属性获取设备地址字符串。 @ppumkinWin32_USBHub
仅包括 USB 集线器,并非所有设备,因此您缺少一些设备。 @nedko 是正确的,因为您需要获取 Win32_USBControllerDevice
的 Dependent 属性才能获取所有设备。【参考方案2】:
我知道我正在回答一个老问题,但我刚刚完成了同样的练习并发现了更多信息,我认为这些信息将对讨论做出很大贡献并帮助其他发现此问题的人看看现有答案的不足之处。
accepted answer 很接近,可以使用Nedko's comment 对其进行更正。更详细地了解所涉及的 WMI 类有助于完成这幅图。
Win32_USBHub
仅返回 USB 集线器。事后看来,这似乎很明显,但上面的讨论忽略了它。它不包括所有可能的 USB 设备,仅包括那些可以(至少在理论上)充当附加设备集线器的设备。它遗漏了一些不是集线器的设备(尤其是复合设备的一部分)。
Win32_PnPEntity
确实包括所有 USB 设备,以及数百个非 USB 设备。 Russel Gantman's 建议使用 WHERE 子句搜索 Win32_PnPEntity
以查找以“USB%”开头的 DeviceID 来过滤列表是有帮助的,但有点不完整;它缺少蓝牙设备、一些打印机/打印服务器以及符合 HID 标准的鼠标和键盘。我见过“USB\%”、“USBSTOR\%”、“USBPRINT\%”、“BTH\%”、“SWD\%”和“HID\%”。但是,Win32_PnPEntity
是一个很好的“主”参考,可以在您拥有其他来源的 PNPDeviceID 后查找信息。
我发现枚举 USB 设备的最佳方法是查询 Win32_USBControllerDevice
。虽然它没有提供设备的详细信息,但它确实完全枚举了您的 USB 设备,并为您提供了每个 USB 设备(包括集线器、非集线器设备和 HID 兼容设备)的前因/从属对 PNPDeviceID
s ) 在您的系统上。从查询返回的每个 Dependent 都将是一个 USB 设备。 Antecedent 将是分配给它的控制器,是通过查询Win32_USBController
返回的 USB 控制器之一。
作为奖励,在后台,WMI 似乎在响应 Win32_USBControllerDevice
查询时会遍历 Device Tree,因此返回这些结果的顺序有助于识别父/子关系。 (这没有记录,因此只是一个猜测;使用 SetupDi API 的 CM_Get_Parent(或 Child + Sibling)以获得明确的结果。)作为 SetupDi API 的一个选项,似乎对于列出的所有设备在Win32_USBHub
下,它们可以在注册表中查找(HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Enum\ + PNPDeviceID
),并且将有一个参数ParentIdPrefix
,它将是其子项的 PNPDeviceID 中最后一个字段的前缀,因此这也可以用于通配符匹配以过滤 Win32_PnPEntity
查询。
在我的应用程序中,我执行了以下操作:
(可选)查询Win32_PnPEntity
并将结果存储在键值映射中(以 PNPDeviceID 作为键)供以后检索。如果您想稍后进行个别查询,这是可选的。
查询Win32_USBControllerDevice
以获取我系统上USB 设备的最终列表(所有从属设备)并提取这些设备的PNPDeviceID。我更进一步,根据设备树的顺序,将设备分配给根集线器(返回的第一个设备,而不是控制器),并基于 parentIdPrefix 构建了一个树。查询返回的顺序(与通过 SetupDi 进行的设备树枚举匹配)是每个根集线器(Antecedent 为其标识控制器),然后是其下的设备迭代,例如,在我的系统上:
第一个控制器的根集线器
第二个控制器的根集线器
第二个控制器的根集线器下的第一个集线器(具有 parentIdPrefix)
第二个控制器的根集线器下的第一个集线器下的第一个复合设备(PNPDeviceID 与集线器的 ParentIdPrefix 以上匹配;有自己的 ParentIdPrefix)
复合设备的 HID 设备部分(PNPDeviceID 与上述复合设备的 ParentIDPrefix 匹配)
第二个控制器的根集线器下的第一个集线器下的第二个设备
复合设备的 HID 设备部分
第二个控制器根集线器下的第二个集线器
第二个控制器的根集线器下的第二个集线器下的第一个设备
第二个控制器根集线器下的第三个集线器
等
查询Win32_USBController
。这为我提供了位于设备树顶部的控制器的 PNPDeviceID 的详细信息(它们是上一个查询的前提)。使用上一步中派生的树,递归迭代其子节点(根集线器)及其子节点(其他集线器)及其子节点(非集线器设备和复合设备)及其子节点等。
通过引用第一步中存储的地图检索了我的树中每个设备的详细信息。 (或者,可以跳过第一步,并使用 PNPDeviceId 单独查询 Win32_PnPEntity
以获取此步骤的信息;可能是 CPU 与内存的权衡,以确定哪个顺序更好。)
总之,Win32USBControllerDevice
Dependents 是系统上 USB 设备的完整列表(除了控制器本身,它们是同一查询中的先行者),并通过交叉引用这些 PNPDeviceId
对与来自注册表和提到的其他查询,可以构建详细的图片。
【讨论】:
如果一台连接了 4 台相同的扫描仪,例如,如果它们用于 4 种不同的操作,您如何区分哪个是哪个? @topshot 只要连接,PNPDeviceID 就是唯一的。没有办法知道您是否断开了一个并在稍后连接了第二个相同的。此 ID 在其他领域也被交叉引用,以希望确定使用了哪个操作。 如果设备有内置序列号,则可以区分设备(这就是序列号的用途)。序列号用作 PnP“实例 ID”。如果设备不包含序列号,则实例 ID 本质上是设备树从根到设备的路径(并且包含“&”字符) 作为后备,总是观察设备列表并在观察变化的同时拔掉和重新插入。【参考方案3】:为了查看我感兴趣的设备,我在 Adel Hazzah 的代码中将 Win32_USBHub
替换为 Win32_PnPEntity
,基于 this post。这对我有用:
namespace ConsoleApplication1
using System;
using System.Collections.Generic;
using System.Management; // need to add System.Management to your project references.
class Program
static void Main(string[] args)
var usbDevices = GetUSBDevices();
foreach (var usbDevice in usbDevices)
Console.WriteLine("Device ID: 0, PNP Device ID: 1, Description: 2",
usbDevice.DeviceID, usbDevice.PnpDeviceID, usbDevice.Description);
Console.Read();
static List<USBDeviceInfo> GetUSBDevices()
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"Select * From Win32_PnPEntity"))
collection = searcher.Get();
foreach (var device in collection)
devices.Add(new USBDeviceInfo(
(string)device.GetPropertyValue("DeviceID"),
(string)device.GetPropertyValue("PNPDeviceID"),
(string)device.GetPropertyValue("Description")
));
collection.Dispose();
return devices;
class USBDeviceInfo
public USBDeviceInfo(string deviceID, string pnpDeviceID, string description)
this.DeviceID = deviceID;
this.PnpDeviceID = pnpDeviceID;
this.Description = description;
public string DeviceID get; private set;
public string PnpDeviceID get; private set;
public string Description get; private set;
【讨论】:
这很好用。为了便于确定您刚刚插入的设备,请将其编写为按时间间隔运行,将条目写入字典,并报告您上次运行时添加的任何内容。【参考方案4】:Adel Hazzah 的 answer 提供了工作代码,Daniel Widdis's 和 Nedko's cmets 提到您需要查询 Win32_USBControllerDevice 并使用它的 Dependent 属性,而 Daniel 的 answer 提供了很多没有代码的细节。
以下是上述讨论的综合,以提供列出所有已连接 USB 设备的可直接访问的 PNP 设备属性的工作代码:
using System;
using System.Collections.Generic;
using System.Management; // reference required
namespace cSharpUtilities
class UsbBrowser
public static void PrintUsbDevices()
IList<ManagementBaseObject> usbDevices = GetUsbDevices();
foreach (ManagementBaseObject usbDevice in usbDevices)
Console.WriteLine("----- DEVICE -----");
foreach (var property in usbDevice.Properties)
Console.WriteLine(string.Format("0: 1", property.Name, property.Value));
Console.WriteLine("------------------");
public static IList<ManagementBaseObject> GetUsbDevices()
IList<string> usbDeviceAddresses = LookUpUsbDeviceAddresses();
List<ManagementBaseObject> usbDevices = new List<ManagementBaseObject>();
foreach (string usbDeviceAddress in usbDeviceAddresses)
// query MI for the PNP device info
// address must be escaped to be used in the query; luckily, the form we extracted previously is already escaped
ManagementObjectCollection curMoc = QueryMi("Select * from Win32_PnPEntity where PNPDeviceID = " + usbDeviceAddress);
foreach (ManagementBaseObject device in curMoc)
usbDevices.Add(device);
return usbDevices;
public static IList<string> LookUpUsbDeviceAddresses()
// this query gets the addressing information for connected USB devices
ManagementObjectCollection usbDeviceAddressInfo = QueryMi(@"Select * from Win32_USBControllerDevice");
List<string> usbDeviceAddresses = new List<string>();
foreach(var device in usbDeviceAddressInfo)
string curPnpAddress = (string)device.GetPropertyValue("Dependent");
// split out the address portion of the data; note that this includes escaped backslashes and quotes
curPnpAddress = curPnpAddress.Split(new String[] "DeviceID=" , 2, StringSplitOptions.None)[1];
usbDeviceAddresses.Add(curPnpAddress);
return usbDeviceAddresses;
// run a query against Windows Management Infrastructure (MI) and return the resulting collection
public static ManagementObjectCollection QueryMi(string query)
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(query);
ManagementObjectCollection result = managementObjectSearcher.Get();
managementObjectSearcher.Dispose();
return result;
如果需要,您需要添加异常处理。如果您想弄清楚设备树等,请查阅 Daniel 的回答。
【讨论】:
【参考方案5】:如果将 ManagementObjectSearcher 更改为以下内容:
ManagementObjectSearcher searcher =
new ManagementObjectSearcher("root\\CIMV2",
@"SELECT * FROM Win32_PnPEntity where DeviceID Like ""USB%""");
所以“GetUSBDevices() 看起来像这样”
static List<USBDeviceInfo> GetUSBDevices()
List<USBDeviceInfo> devices = new List<USBDeviceInfo>();
ManagementObjectCollection collection;
using (var searcher = new ManagementObjectSearcher(@"SELECT * FROM Win32_PnPEntity where DeviceID Like ""USB%"""))
collection = searcher.Get();
foreach (var device in collection)
devices.Add(new USBDeviceInfo(
(string)device.GetPropertyValue("DeviceID"),
(string)device.GetPropertyValue("PNPDeviceID"),
(string)device.GetPropertyValue("Description")
));
collection.Dispose();
return devices;
您的结果将仅限于 USB 设备(而不是您系统上的所有类型)
【讨论】:
搜索以 USB 开头的 deviceID 的 where 子句遗漏了一些项目。最好迭代“Win32_USBControllerDevice”的Dependents【参考方案6】:对于只寻找可移动 USB 驱动器的人来说,这是一个简单得多的示例。
using System.IO;
foreach (DriveInfo drive in DriveInfo.GetDrives())
if (drive.DriveType == DriveType.Removable)
Console.WriteLine(string.Format("(0) 1", drive.Name.Replace("\\",""), drive.VolumeLabel));
【讨论】:
也会返回一张软盘,可能是 USB 读卡器,可能是 Zip、Jazz 和 Orb 驱动器 对于只想匹配 USB 友好名称的人来说,这是理想的解决方案。我使用此示例进行数据备份,由于驱动器号更改,我需要查找名称(此处为 drive.VolumeLabel) 这将包括 eSATA、Fireware、Thunderbolt 硬盘驱动器,以及带有可移动介质(如 CD、DVD 和蓝光驱动器)的内部 SATA、iDE 和 SCSI 驱动器。【参考方案7】:您可能会发现this thread 很有用。这是一个 google code project 的例子(它 P/Invokes into setupapi.dll
)。
【讨论】:
你知道为什么我使用 System.Management 的 ObjectQuery 类没有引用吗? @Robert 你添加了对项目的引用吗?您可以通过右键单击项目中的引用 > 添加引用... > 搜索并检查 System.Management > 确定来做到这一点。【参考方案8】: lstResult.Clear();
foreach (ManagementObject drive in new ManagementObjectSearcher("select * from Win32_DiskDrive where InterfaceType='USB'").Get())
foreach (ManagementObject partition in new ManagementObjectSearcher("ASSOCIATORS OF Win32_DiskDrive.DeviceID='" + drive["DeviceID"] + "' WHERE AssocClass = Win32_DiskDriveToDiskPartition").Get())
foreach (ManagementObject disk in new ManagementObjectSearcher("ASSOCIATORS OF Win32_DiskPartition.DeviceID='" + partition["DeviceID"] + "' WHERE AssocClass = Win32_LogicalDiskToPartition").Get())
foreach (var item in disk.Properties)
object value = disk.GetPropertyValue(item.Name);
string valor = disk["Name"].ToString();
lstResult.Add(valor);
【讨论】:
object value
是做什么的?
浏览磁盘上的其他可用属性,并将其值保存在对象值中以上是关于获取连接的 USB 设备列表的主要内容,如果未能解决你的问题,请参考以下文章