Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drive Letter to Device Instance ID

How do I get from a drive letter to a device instance ID?

My process starts with a device arrival message. I have been successful in getting the drive letter from the arrival message and in opening the dvd tray.

I have searched the various Setup API items; but I haven't found anything that gets me from a drive letter to a device instance ID.

A solution in C# or VB.NET would be ideal, but I'm willing to figure it out from any other language as long as I can see the API calls.

Thanks in advance...

like image 564
SteveS48 Avatar asked Oct 29 '25 18:10

SteveS48


2 Answers

You cannot do it directly.

The link is to use STORAGE_DEVICE_NUMBER. You can use DeviceIoControl with IOCTL_STORAGE_GET_DEVICE_NUMBER on your device name to populate this structure. Put this value to one side.
You then need to get device infomation on your system using SetupDiGetClassDevs setting the GUIDS as approriate, indicicating the drives your are insterested in. Then enumerate through the devices using SetupDiEnumDeviceInfo. Then enumerate the interfaces using SetupDiEnumDeviceInterfaces and finally get the information using SetupDiGetDeviceInterfaceDetail. In this structure returned you can get a DevicePath you can use to get the STORAGE_DEVICE_NUMBER as above. Match this with the STORAGE_DEVICE_NUMBER from your drive letter, and you have now linked a driver letter to your structure. Phew! Inside this structure is a DevInst.

like image 168
DanDan Avatar answered Oct 31 '25 10:10

DanDan


I know it's years later but I had to do this and searching brought me here and @DanDan 's answer worked. In order to save future people a lot of work, I thought I'd give back a little and present the technique a bit more explicitly. You'll still have to write a bit of code, but the part I found difficult is below as code:

As DanDan mentioned, the idea is to use CreateFile and DeviceIoControl to get the Windows STORAGE_DEVICE_NUMBER for the disk associated with a file path, and then use the Setup API to enumerate disk devices until we find one whose device instance equals the SDN.

First, here's a summary of how you get the STORAGE_DEVICE_NUMBER from the path (e.g. c:\\users\\bob);

  1. Strip the path to the root (e.g down to C:) and prepend it with \\\\.\\ so you have \\\\.\\C:
  2. Open that path up using CreateFileW with to get metadata
  3. Use DeviceIoControl with IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS to get the extents
  4. Get the DiskNumber member from the first extent returned.
  5. Close the file
  6. Open up \\\\.\\PhysicalDrive<n> where <n> is the that DiskNumber from the first extent
  7. Use DeviceIoControl with code IOCTL_STORAGE_GET_DEVICE_NUMBER to get make it fill out a STORAGE_DEVICE_NUMBER struct as output
  8. Use SetupDiGetClassDevs with arguments &GUID_DEVCLASS_DISKDRIVE and DICGF_PRESENT to get all disks on the system
  9. In a loop, use SetupDiEnumDeviceInfo to get a SP_DEVINFO_DATA repeatedly (on the device list returned by step #8 above) and a call the function below to determine which one, if any, matches the STORAGE_DEVICE_NUMBER for the give path.

(This is edited to remove custom utility classes of mine right on the SO web page so I might have introduced errors/typos)

bool DoesDeviceInstanceEqualStorageDeviceNumber(
    const std::string&      devInstance, 
    STORAGE_DEVICE_NUMBER   sdn)
{   
    // Open up this device instance, specifying that we want the *interfaces*.
    // The interfaces are key key because examining them will let us get a 
    // string we can use the Win32 CreateFile function.  

    const auto hDevInfo = SetupDiGetClassDevsA(
        nullptr,
        devInstance.c_str(), 
        nullptr, 
        DIGCF_DEVICEINTERFACE | DIGCF_ALLCLASSES);

    if (hDevInfo == INVALID_HANDLE_VALUE)
        throws std::runtime_error("Unable to get disk devices");


    DWORD dwSize = 0;
    SP_DEVINFO_DATA did;
    WCHAR buffer[4096];

    did.cbSize = sizeof (did);
    bool foundValidMatch = false;
    int deviceNumber = 0;

    // Iterate through all such devices, looking for one that has a storage device number that matches the given one.

    while ( !foundValidMatch &&  SetupDiEnumDeviceInfo(hDevInfo, deviceNumber, &did))
    {
        deviceNumber++;

        DEVPROPTYPE devPropType;

        // We'll only bother comparing this one if it is fixed.  Determine that.

        const auto getPropResult = SetupDiGetDevicePropertyW (
            hDevInfo, 
            &did, 
            &DEVPKEY_Device_RemovalPolicy,  // Ask for the "removal policy"
            &devPropType, 
            (BYTE*)buffer, 
            sizeof(buffer), 
            &dwSize, 
            0);

        if (!getPropResult)
        {
            std::cerr << "Unable to to get removal policy for disk device: " << ::GetLastError() << std::endl;
            continue;
        }

        /*  This bit *would* skip removable disks, you wanted...
        else if (buffer[0] != 1)
        {
  
            std::cerr << "Skipping removable disk device " << devInstance << std::endl;
            continue;
        }
        */

        // OK this is a fixed disk so it might be the one we'll compare against
        // 1. Get the very first disk interface from this particular disk device
        // 2. Open a file on it
        // 3. Query the resulting file for its device number.
        // 4. Compare the device number to the one we determined above
        // 5. If it matches ours, then we succeed.  If not, continue

        SP_DEVICE_INTERFACE_DATA devIntData;
        devIntData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);

        // Get the disk interfaces
        const auto result = SetupDiEnumDeviceInterfaces(
            hDevInfo,
            &did, //&did,
            &GUID_DEVINTERFACE_DISK, // Get Disk Device Interface (from winioctl.h)
            0,                       // We only need the very FIRST one.  I think...
            &devIntData);

        if (!result)
            continue;

        DWORD dwRequiredSize = 0;

        // Want to get the detail but don't yet know how much space we'll need
        // Do a dummy call to find out

        SetupDiGetDeviceInterfaceDetail(
            hDevInfo, 
            &devIntData, 
            nullptr, 
            0, 
            &dwRequiredSize, 
            nullptr);

        if (ERROR_INSUFFICIENT_BUFFER != ::GetLastError())
        {
            std::cerr << "Unable to get device interface Detail: " << ::GetLastError() << std::endl;;
        }
        else
        {

            // Get the detail data so we can get the device path and open a file.

            std::vector<TCHAR> buf(dwRequiredSize);
            auto pDidd = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(buf.data());

            // WARNING: HARD CODED HACK
            // ------------------------
            // https://stackoverflow.com/questions/10405193/vb-net-hid-setupdigetdeviceinterfacedetail-getlasterror-shows-1784-error-inv
            //
            // Don't ask.  Just do what they tell you. 
            // -----------------------------------------------------------------
#ifdef BUILD_64
            pDidd->cbSize = 8;
#else
            pDidd->cbSize = 6;
#endif
            // -----------------------------------------------------------------

            if (!SetupDiGetDeviceInterfaceDetail(
                hDevInfo, 
                &devIntData, 
                pDidd, 
                dwRequiredSize, 
                &dwRequiredSize, 
                nullptr))
            {
                std::cerr << "Cannot get interface detail: " << ::GetLastError());
            }
            else
            {
                // FINALLY:  We now have a DevicePath that we can use to open up
                // in a Win32 CreateFile() call.   That will let us get the
                // STORAGE_DEVICE_NUMBER and compare it to the one we were given.

                const auto hFile = ::CreateFileW(pDidd->DevicePath, 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, NULL);
                if (INVALID_HANDLE_VALUE != hFile)
                {
                    std::cerr << "Unable to open logical volume: " + devicePath << std::endl;
                    continue;
                }
 
                STORAGE_DEVICE_NUMBER sdnTest;
                ZeroMemory(&sdnTest, sizeof(STORAGE_DEVICE_NUMBER));


                if (0 == DeviceIoControl(
                     hDevInfo
                     IOCTL_STORAGE_GET_DEVICE_NUMBER,
                     nullptr,            // output only so not needed
                     0,                  // output only so not needed
                     &sdnTest,
                     sizeof(STORAGE_DEVICE_NUMBER),
                     nullptr, 
                     nullptr))
                {
                    std::cerr << "Unable to determine storage device number: " << ::GetLastError() << std::endl;);
                }
                else
                {
                    // All this for a one-line test...

                    foundValidMatch = sdnTest.DeviceNumber == sdn.DeviceNumber; 
                }
            }
        }
    }
    SetupDiDestroyDeviceInfoList(hDevInfo);
    return foundValidMatch;
}

I hope this saves someone a headache

like image 34
Joe Avatar answered Oct 31 '25 10:10

Joe



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!