Drive Letter to Device Instance ID

后端 未结 3 871
刺人心
刺人心 2021-01-14 14:04

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 th

3条回答
  •  忘掉有多难
    2021-01-14 14:26

    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 where 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 buf(dwRequiredSize);
                auto pDidd = reinterpret_cast(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

提交回复
热议问题