如何使用WMI获取USB设备的驱动器字母

4

我需要从C#应用程序中跟踪USB插入和拔出事件,因此我根据SO上的其他问题提出了以下想法。

我无法使用这种方法。

var drives = DriveInfo.GetDrives()
    .Where(drive => drive.IsReady && drive.DriveType == DriveType.Removable).ToList();

由于连接和断开的设备可能具有相同的驱动器字母和名称(新的设备可能与之前缓存的设备相同),因此在区分连接和断开的设备时可能会引起许多麻烦。

因此,我决定使用 WMI 来解决这个问题,但我发现通过 Win32_USBHub 类无法获取指定 USB 设备的驱动器字母。然后我想执行另一个类似于以下查询的查询:

foreach (ManagementObject device in new ManagementObjectSearcher(@"SELECT * FROM Win32_USBHub").Get())
{
    string deviceID = (string)device.GetPropertyValue("DeviceID");

    Console.WriteLine("{0}", deviceID);

    string query = string.Format("SELECT * FROM Win32_LogicalDisk WHERE DeviceID='{0}'", deviceID);
    foreach (ManagementObject o in new ManagementObjectSearcher(query).Get())
    {
        string name = (string)o.GetPropertyValue("Name");
        Console.WriteLine("{0}", name);
    }

    Console.WriteLine("==================================");
}

但它根本不起作用——每次我尝试执行与Win32_LogicalDisk表一起使用的查询时,都会出现异常"无效查询"。

为什么?我做错了什么?该怎么解决?也许有更好的方法来解决这个问题?

提前感谢您。


https://dev59.com/wlnUa4cB1Zd3GeqPXh4I?rq=1 这篇文章回答了您的问题吗? - MSalters
@MSalters string query = string.Format("ASSOCIATORS OF {{Win32_USBHub.DeviceID = '{0}'}} WHERE AssocClass = Win32_LogicalDisk", deviceID); foreach (var o in new ManagementObjectSearcher(query).Get()) { string name = (string)o.GetPropertyValue("Name"); Console.WriteLine("{0}", name); } 给我抛出了一个“无效的对象路径”异常。 - FrozenHeart
2个回答

11

你之所以会收到异常是因为你的deviceID包含需要转义(反斜杠)的字符。使用简单的替换操作,你就不会再收到异常了。

string query = string.Format("SELECT * FROM Win32_LogicalDisk WHERE DeviceID='{0}'", deviceID.Replace(@"\", @"\\"));

然而,从WMI获取USB驱动器的盘符有些复杂。你需要遵循几个类,就像@MSalters在评论中提到的链接所述:
Win32_DiskDrive-> Win32_DiskDriveToDiskPartition -> Win32_DiskPartition -> Win32_LogicalDiskToPartition -> Win32_LogicalDisk.

我在这里找到了一段稍微修改过的代码,对我很有效:

foreach (ManagementObject device in new ManagementObjectSearcher(@"SELECT * FROM Win32_DiskDrive WHERE InterfaceType LIKE 'USB%'").Get())
{
    Console.WriteLine((string)device.GetPropertyValue("DeviceID"));
    Console.WriteLine((string)device.GetPropertyValue("PNPDeviceID"));                

    foreach (ManagementObject partition in new ManagementObjectSearcher(
        "ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + device.Properties["DeviceID"].Value
        + "'} WHERE AssocClass = Win32_DiskDriveToDiskPartition").Get())
    {
        foreach (ManagementObject disk in new ManagementObjectSearcher(
                    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID='"
                        + partition["DeviceID"]
                        + "'} WHERE AssocClass = Win32_LogicalDiskToPartition").Get())
        {
            Console.WriteLine("Drive letter " + disk["Name"]);
        }
    }
}

1
谢谢你的回答。顺便问一下,使用PNPDeviceID而不是DeviceID有什么意义?这是解决原始问题的最佳方法吗? - FrozenHeart
我只是为了更清晰的输出打印了“PNPDeviceID”,它并没有在搜索过程中使用。据我所知,“PNPDeviceID”是由即插即用管理器使用的某种标准格式,而“DeviceID”的格式因类而异。这是最好的方法吗?我不知道。但考虑到需要遍历的类别数量,这肯定是最短的之一。 - michalk

1
这是对C# 8的michalk答案进行重构后的版本。
我将其与使用Windows服务和C#检测USB驱动器插入和拔出相关问题的答案结合使用。
        static IEnumerable<(string deviceId, string pnpDeviceId, string driveLetter)> SelectDeviceInformation()
        {
            foreach (ManagementObject device in SelectDevices())
            {
                var deviceId = (string)device.GetPropertyValue("DeviceID");
                var pnpDeviceId = (string)device.GetPropertyValue("PNPDeviceID");
                var driveLetter = (string)SelectPartitions(device).SelectMany(SelectDisks).Select(disk => disk["Name"]).Single();

                yield return (deviceId, pnpDeviceId, driveLetter);
            }

            static IEnumerable<ManagementObject> SelectDevices() => new ManagementObjectSearcher(
                    @"SELECT * FROM Win32_DiskDrive WHERE InterfaceType LIKE 'USB%'").Get()
                .Cast<ManagementObject>();

            static IEnumerable<ManagementObject> SelectPartitions(ManagementObject device) => new ManagementObjectSearcher(
                    "ASSOCIATORS OF {Win32_DiskDrive.DeviceID=" +
                    "'" + device.Properties["DeviceID"].Value + "'} " +
                    "WHERE AssocClass = Win32_DiskDriveToDiskPartition").Get()
                .Cast<ManagementObject>();

            static IEnumerable<ManagementObject> SelectDisks(ManagementObject partition) => new ManagementObjectSearcher(
                    "ASSOCIATORS OF {Win32_DiskPartition.DeviceID=" +
                    "'" + partition["DeviceID"] + "'" +
                    "} WHERE AssocClass = Win32_LogicalDiskToPartition").Get()
                .Cast<ManagementObject>();
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接