Find Windows Drive Letter of a removable disk from USB VID/PID

.net, c#

Solution

I had exactly the same problem and after a hard week of work I finally got the solution. I also get two links for one device (the link with the vid and pid and a link, which contains "USBSTOR...."). I think it's not the best way to solve the problem, but (until now) it works.

I used two functions:

The first one is to find a USBHub with a specific VID and PID and also find the related second link ("USBSTOR...."). This link is important, because it forms the connection to the drive letter. A list named "USBobjects" contains a number of related links (USBHub, USBSTOR,....), which refer to all attached devices. I found out, that the USBSTOR link appears right after the link, which contains the VID and PID. I stored the "USBStOR..." link as a string and used it for the second function to find the related PNPEntity of the DiskDrive. This leads to the correct DiskPartition and furthermore to the LogicalDisk = drive letter.

Hopefully it's not to late and both functions will help u!

FIRST FUNCTION:

    public void FindPath()
    {
        foreach (ManagementObject entity in new ManagementObjectSearcher("select * from Win32_USBHub Where DeviceID Like '%VID_XXXX&PID_XXXX%'").Get())
        {
            Entity = entity["DeviceID"].ToString();

            foreach (ManagementObject controller in entity.GetRelated("Win32_USBController"))
            {
                foreach (ManagementObject obj in new ManagementObjectSearcher("ASSOCIATORS OF {Win32_USBController.DeviceID='" + controller["PNPDeviceID"].ToString() + "'}").Get())
                {
                    if(obj.ToString().Contains("DeviceID"))
                        USBobjects.Add(obj["DeviceID"].ToString());

                }
            }

        }

        int VidPidposition = USBobjects.IndexOf(Entity);
        for (int i = VidPidposition; i <= USBobjects.Count; i++ )
        {
            if (USBobjects[i].Contains("USBSTOR"))
            {
                Secondentity = USBobjects[i];
                break;
            }

        }
    }

>

SECOND FUNCTION:

    public void GetDriveLetter()
    {
        foreach (ManagementObject drive in new ManagementObjectSearcher("select * from Win32_DiskDrive").Get())
            {
                if (drive["PNPDeviceID"].ToString() == Secondentity)
                {
                    foreach (ManagementObject o in drive.GetRelated("Win32_DiskPartition"))
                    {
                        foreach (ManagementObject i in o.GetRelated("Win32_LogicalDisk"))
                        {
                            Console.WriteLine("Disk: " + i["Name"].ToString());
                        }
                    }
                }
        }
    }

>

Problem

This question has been asked before, and there is one answer that supposedly works here. But I've tried it out and it does not work for me. The issue is that the PNPDeviceID returned by the Query on Win32_DiskDrive and that returned by the "Device" class are different. For example in my case the Query returns something like - PNPDeviceID: USBSTOR\DISK&VEN_ABCD&PROD_1234&REV_0001\8&2C3C9390&0 while the Device class returns the actual VID/PID combination --> USB\VID_4568&PID_QWER&MI_00\7&15b8d7f0&3&0000. So the SELECT query on Win32_DiskDrive always fails. Main Code: ``` var usbDevices = GetUSBDevices(); //Enumerate the USB devices to see if any have specific VID/PID foreach (var usbDevice in usbDevices) { if (usbDevice.DeviceID.Contains("ABCD") && usbDevice.DeviceID.Contains("1234")) { foreach (string name in usbDevice.GetDiskNames()) { //Open dialog to show file names Debug.WriteLine(name); } } } ``` USBDeviceInfo Class ``` 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; } public IEnumerable<string> GetDiskNames() { using (Device device = Device.Get(PnpDeviceID)) { // get children devices foreach (string childDeviceId in device.ChildrenPnpDeviceIds) { // get the drive object that correspond to this id (escape the id) Debug.WriteLine(childDeviceId.Replace(@"\", @"\\") ); foreach (ManagementObject drive in new ManagementObjectSearcher("SELECT DeviceID FROM Win32_DiskDrive WHERE PNPDeviceID='" + childDeviceId.Replace(@"\", @"\\") + "'").Get()) { foreach (PropertyData usb in drive.Properties){ if (usb.Value != null && usb.Value.ToString() != "") { Debug.Write(usb.Name + "="); Debug.Write(usb.Value + "\r\n"); } } // associate physical disks with partitions foreach (ManagementObject partition in new ManagementObjectSearcher("ASSOCIATORS OF {Win32_DiskDrive.DeviceID='" + drive["DeviceID"] + "'} WHERE AssocClass=Win32_DiskDriveToDiskPartition").Get()) { // associate partitions with logical disks (drive letter volumes) foreach (ManagementObject disk in new ManagementObjectSearcher("ASSOCIATORS OF {Win32_DiskPartition.DeviceID='" + partition["DeviceID"] + "'} WHERE AssocClass=Win32_LogicalDiskToPartition").Get()) { yield return (string)disk["DeviceID"]; } } } } } } ``` As a side note, I am able to get the Query using "Model" property pass and then find the drive letter, as explained here. But I'm looking for a solution that can tie VID/PID to the drive letter.

Original source