1

I am trying to get USB flash drive ID using this code:

ManagementObjectSearcher theSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
foreach (ManagementObject currentObject in theSearcher.Get())
{                    
    Console.WriteLine("PNPDeviceID: " + currentObject["PNPDeviceID"]);                    
}

On most of computers I will get something like this: USBSTOR\DISK&VEN_TQ&PROD_S1&REV_1.10\11100049977&0

but on some systems for the same USB drive I get something like this: USBSTOR\DISK&VEN_TQ&PROD_S1&REV_1.10\ 6&2D2B8A01&0& 11100049977&0

Note that the part 6&2D2B8A01&0& changes depending on port that USB drive is inserted in.

How can I get the first version of the ID on every system, regardless of port USB drive is inserted in?

UPDATE 1: when using Win32_DiskDrive USB drive is detected on every PC. But when using Win32_USBHub USB drive is not detected on problematic PCs.

UPDATE 2: when using SystemUSBDrives class from this answer, on problematic PCs I get this output:

Port 1:

SystemUSBDrives PNPDeviceID: USBSTOR\DISK&VEN_TQ&PROD_S1&REV_1.10\6&2D2B8A01&0&11100049977&0
SystemUSBDrives DeviceID: \\.\PHYSICALDRIVE2
SystemUSBDrives SerialNumber:  
SystemUSBDrives VolumeSerialNumber: D6533504

Port 2:

SystemUSBDrives PNPDeviceID: USBSTOR\DISK&VEN_TQ&PROD_S1&REV_1.10\6&7A722D3&0&11100049977&0
SystemUSBDrives DeviceID: \\.\PHYSICALDRIVE2
SystemUSBDrives SerialNumber:  
SystemUSBDrives VolumeSerialNumber: D6533504

Port 3:

SystemUSBDrives PNPDeviceID: USBSTOR\DISK&VEN_TQ&PROD_S1&REV_1.10\6&32CECE73&0&11100049977&0
SystemUSBDrives DeviceID: \\.\PHYSICALDRIVE2
SystemUSBDrives SerialNumber:  
SystemUSBDrives VolumeSerialNumber: D6533504

Using this on other computers returns correct SystemUSBDrives SerialNumber value.

zoran
  • 943
  • 11
  • 22
  • That "number" is called `ParentIdPrefix` and is added to ensure that a `PNPDeviceId` is unique in a system. A device is not required to have a serial number. A `DeviceInstanceID` is used in this case. See this class here: [Get serial number of usb storage device](https://stackoverflow.com/questions/49118708/get-serial-number-of-usb-storage-device-in-net-core-2-1?answertab=active#tab-top) (it doesn't just get the serial number). The question is related to .Net Core 2.1, but it's the same on other frameworks that support WMI queries. – Jimi Oct 04 '18 at 10:11
  • @Jimi THank you for your response. I tried and unfortunately it didn't work. Please check the UPDATE 2 in the question. – zoran Oct 04 '18 at 13:18
  • This could be a quite wide matter. In my experience, sometimes the USB SerialNumber is written using a wrong encoding. Some Unicode characters are inserted. This can throw off some USB controllers. In some cases, the SerialNumber is shown, but garbled. In Windows 7 there was an update that handled part of the issue. As part of Service Pack 1 and a cumulative update (sort of a Service Pack 2). But of course I'm not sure if this is the case. Maybe, post a reference that can identify the USB controllers or some common hardware in those PCs that show this issues. – Jimi Oct 04 '18 at 14:10
  • You could also give it a shot with [DeviceIoControl](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363216%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396). It uses a different method (at least in reading the device strings). You need to open the device with [CreateFile()](https://learn.microsoft.com/en-us/windows/desktop/api/fileapi/nf-fileapi-createfilew), passing the device name as `"\\.\F"`, where `F` is the Logical Disk DeviceID (or, simply, the `F:` disk). – Jimi Oct 04 '18 at 15:26
  • @Jimi it seems that it is for C++. I am thinking to just parse the string and remove the ParentIdPrefix. Is it safe to assume that the ParentIdPrefix is located between last \ char and the second & char from the end in the PNPDeviceID string? – zoran Oct 05 '18 at 07:42
  • Well, `DeviceIOControl` can be called from C#, no problem. These are [the declarations on PInvoke.net](https://www.pinvoke.net/default.aspx/kernel32.deviceiocontrol). But 1) If you've never used that function, it can be a pain 2) No success whatsoever is granted. If you want to used that string for whatever reason, keep both the `ParentIdPrefix` and what could be the `DeviceInstanceID` (included the last char on the right) to ensure that this "serial" is unique. But of course it won't match the actual serial number when read correctly. Your choice. – Jimi Oct 05 '18 at 07:51
  • When using your class on "good" PCs I get SerialNumber and the PNPDeviceID that ends with the same SerialNumber. But when I use your class on the "bad" PCs I get only PNPDeviceID that ends with ParentIdPrefix + SerialNumber. So I'm thinking that I could extract the SerialNumber in all cases from the PNPDeviceID... – zoran Oct 05 '18 at 12:12
  • What I meant was: since this is a sort of a state of indetermination, maybe keep both. You might have, say, a class, that contains both the informations. If a field/property containing the prefix is non-empty, you know that "SerialNumber" is, possibly, a `DeviceInstanceID`. Thus, you can make different decisions base on this information. Just a suggestion. I don't really know what you're doing with this data and how/where/when it's used. – Jimi Oct 05 '18 at 12:35

2 Answers2

0

With DriveInfo you can get all driver information.

look here DriveType

 var drivers = DriveInfo.GetDrives() //all Drivers
                    .Where(x => x.DriveType == DriveType.Removable); //Filter Removable Drivers

enter image description here

or if you need PNPDeviceID

var deviceSearcher =
            new ManagementObjectSearcher("SELECT * FROM Win32_USBHub");
        foreach (var o in deviceSearcher.Get())
        {
            var usbDevice = (ManagementObject)o;
            var pnpDeviceId = usbDevice.Properties["PNPDeviceID"].Value.ToString();
        }
go..
  • 958
  • 7
  • 15
  • Unfortunately when using Win32_USBHub as in your example, USB drive is not detected at all on problematic computers. – zoran Oct 04 '18 at 10:02
0

I ended up removing ParentIdPrefix from the string and it works well for my scenario:

public static string RemoveParentIdPrefix(string pnpDeviceId)
{
    int iSplit = pnpDeviceId.LastIndexOf("\\", StringComparison.InvariantCulture);
    string part1 = pnpDeviceId.Substring(0, iSplit);
    string part2 = pnpDeviceId.Substring(iSplit);
    int ampersandCount = 0;
    for (int i = part2.Length - 1; i >= 0; i--)
    {
        if (part2[i] == '&')
        {
            ampersandCount++;
        }

        if (ampersandCount == 2)
        {
            part2 = part2.Substring(i + 1);
            break;
        }
    }
    return part1 + "\\" + part2;
}
zoran
  • 943
  • 11
  • 22