Как получить букву диска USB-устройства с помощью WMI

Мне нужно отслеживать события вставки и удаления USB из приложения C#, поэтому я предложил следующие идеи, основанные на других вопросах, касающихся SO.

Я не могу использовать этот метод

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

потому что это может привести к большому количеству проблем, когда вам нужно различать подключенные и отключенные устройства (новые могут иметь одинаковую букву и имя диска, как те, которые были ранее кэшированы).

Поэтому я решил использовать WMI для решения этой проблемы, но обнаружил, что невозможно получить букву диска указанного USB-устройства через Win32_USBHub учебный класс. Тогда я подумал, что могу выполнить еще один запрос, как этот

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 Таблица.

Зачем? Что я делаю неправильно? Как я могу это исправить? Может быть, есть лучшие способы решить эту проблему?

Заранее спасибо.

1 ответ

Решение

Вы получаете и исключение, потому что ваш deviceID содержит символы, которые должны быть экранированы (обратная косая черта). С простой заменой вы не должны получить исключение.

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

Однако получить букву USB-накопителя от WMI немного сложнее. Вам нужно пройти несколько классов, как указано в ссылке, которую @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"]);
        }
    }
}

Это переработанная версия ответа michalk для C# 8.

Я использовал его в сочетании с ответами на это: Обнаружение вставки и удаления USB-накопителя с помощью службы Windows и С #, связанный вопрос

        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>();
        }
Другие вопросы по тегам