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
i know it's late for you now but not for everybody ^^
I had the same need and this is main line of how I did it:
-You need a window to receive device arrival and removal (as you said)
-Then you create a DeviceNotificationFilter initiated to dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
-Then in the message loop of your window you look for VM_DEVICECHANGE
-When u receive it if wParam == DBT_DEVICEARRIVAL, use the lParam to check if it is a DBT_DEVTYPE_VOLUME (i was getting the letter and the type of the drive here) or a DBT_DEVTYPE_DEVICEINTERFACE ( there you can use your wellcasted lParam to get the InstanceId from the input structure).
When you connect a drive your receive DEVINTERFACE first then the other. I only give the main line beacause i did this long time ago and i don't have the code here, and also I had found a lot of code pieces on the net (long time ago so there should be more now ^^^) maybe msdn give a full code example to do that now.
If you read this and need more informations, i'll reply or make a full documented answer if many need it.
Hope it will help some of you.
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
);
C:
) and prepend it with \\\\.\\
so you have \\\\.\\C:
CreateFileW
with to get metadataDeviceIoControl
with IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS
to get the extentsDiskNumber
member from the first extent returned.\\\\.\\PhysicalDrive<n>
where <n>
is the that DiskNumber from the first extentDeviceIoControl
with code IOCTL_STORAGE_GET_DEVICE_NUMBER
to get make it fill out a STORAGE_DEVICE_NUMBER
struct as outputSetupDiGetClassDevs
with arguments &GUID_DEVCLASS_DISKDRIVE
and DICGF_PRESENT
to get all disks on the systemSetupDiEnumDeviceInfo
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
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.