From: trejkaz@gmail.com on
Our aim is to find the device instance ID for a USB flash drive so that
we can use it for certain purposes.

We have some native code in our system which basically (and
erroneously) does this:

// Use the drive letter to get the volume name.
TCHAR Drive[] = TEXT("C:\\");
Drive[0] = driveLetter;
TCHAR volumeNameBuf[MAX_VOLUME_NAME_SIZE];
rc = GetVolumeNameForVolumeMountPoint(Drive, volumeNameBuf,
MAX_VOLUME_NAME_SIZE);

// Use the volume name to get the storage device number.
STORAGE_DEVICE_NUMBER sdn;
DWORD returned;
// This null removes the slash from the end, which is a
// requirement for DeviceIoControl.
volumeNameBuf[lstrlen(volumeNameBuf) - 1] = '\0';
fileHandle = CreateFile(volumeNameBuf, 0, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
rc = DeviceIoControl(fileHandle,
IOCTL_STORAGE_GET_DEVICE_NUMBER,
NULL, 0, &sdn, sizeof(sdn), &returned, NULL);

// Get the list of all storage devices.
hDevInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_DISK,
NULL, NULL, DIGCF_DEVICEINTERFACE|DIGCF_PRESENT);

// Use the device number to index into the list of storage
// devices to get the specific device.
rc = SetupDiEnumDeviceInfo(hDevInfo, sdn.DeviceNumber,
&deviceInfoData);

The bug in the code is that it assumes that the storage device number
can be used to index into the list of storage devices. Unfortunately,
such an assumption is only true when there is exactly one USB storage
device attached to the system.

So I know what's wrong, but I can't find anything in the API for
finding the _disk_ device number, given a _storage_ device number. How
can I do something like that?

TX

From: Robert Marquardt on
Does this old message help?
It matches the mount points against the enumerated volumes.
The main trick is getting the grandparent node of the volume which is
the USB node. That is also the node to call CM_Request_Device_Eject on.

Subject:
RE: How to match between physical usb device and its drive letter?
From:
"Kiran" <Kiran(a)discussions.microsoft.com>
Date:
Wed, 20 Oct 2004 22:01:02 -0700
Newsgroups:
microsoft.public.development.device.drivers

Hi,

There is no simple/direct way of obtaining the drive letter.

The following code can be used to get the drive letter
But you have to modify them here and there to meet you need.

Kiran


struct tagDrives
{
WCHAR letter;
WCHAR volume[BUFFER_SIZE];
} g_drives[26];

//
WCHAR GetUSBDrive()
{
LPTSTR lpDevID;
WCHAR cDrive;
DWORD dwSize = BUFFER_SIZE;

// Get all removable disks on user laptop.
if(!GetAllRemovableDisks())
{
WRITELOG("Error - GetAllRemovableDisks failed\n");
return 0;
}

// Alocate memory to device ID
lpDevID = (LPTSTR)AllocMem(BUFFER_SIZE);

// Get device ID corresponding to USBFM from registry.
if(!GetRegValue(lpDevID, DEVICE_ID, dwSize))
{
WRITELOG("Error - Registry - USBFMDevID failed\n");
FreeMem(lpDevID);
return 0;
}

// Get drive corresponding to the registry entry.
cDrive = GetSpecificDrive(lpDevID);

FreeMem(lpDevID);

// return the drive letter.
return cDrive;
}

/******************************************************************************
* GetAllRemovableDisks - This function retrieves volume information for all
removable disks
*
* In: None
*
* Out: TRUE - Success
* FALSE - Failure
*
*******************************************************************************/

BOOL GetAllRemovableDisks()
{
WCHAR caDrive[4];
WCHAR volume[BUFFER_SIZE];
int nLoopIndex;
DWORD dwDriveMask;

caDrive[0] = 'A';
caDrive[1] = ':';
caDrive[2] = '\\';
caDrive[3] = 0;

g_count = 0;

// Get all drives in the system.
dwDriveMask = GetLogicalDrives();

if(dwDriveMask == 0)
{
WRITELOG("Error - GetLogicalDrives failed\n");
return FALSE;
}

// Loop for all drives (MAX_DRIVES = 26)

for(nLoopIndex = 0; nLoopIndex< MAX_DRIVES; nLoopIndex++)
{
// if a drive is present,
if(dwDriveMask & 1)
{
caDrive[0] = 'A' + nLoopIndex;

// If a drive is removable
if(GetDriveType(caDrive) == DRIVE_REMOVABLE)
{
//Get its volume info and store it in the global variable.
if(GetVolumeNameForVolumeMountPoint(caDrive, volume, BUFFER_SIZE))
{
g_drives[g_count].letter = caDrive[0];
wcscpy(g_drives[g_count].volume, volume);
g_count ++;
}

}
}
dwDriveMask >>= 1;
}

// success if atleast one removable drive is found.
if(g_count == 0)
{
return FALSE;
}
else
{
return TRUE;
}
}

/******************************************************************************
* GetSpecificDrive - This function returns the drive corresponding to the
given device ID
*
* In : lpDevID - Device ID
*
* Return: Drive letter corresponding to the given device ID.
*
*******************************************************************************/

WCHAR GetSpecificDrive(
LPTSTR lpDevID)
{
HDEVINFO hDevInfo;
GUID guid;
BYTE buffer[BUFFER_SIZE];
DWORD dwRequiredSize ;
WCHAR buf[BUFFER_SIZE];
DEVINST devInstParent;
DWORD dwIndex;
WCHAR volume[BUFFER_SIZE];
int nLength,nLoopIndex;

SP_DEVICE_INTERFACE_DATA devInterfaceData;
SP_DEVINFO_DATA devInfoData;
PSP_DEVICE_INTERFACE_DETAIL_DATA pDevDetail;

if(!lpDevID)
{
return 0;
}

// GUID_DEVINTERFACE_VOLUME is interface Guid for Volume class devices.
guid = GUID_DEVINTERFACE_VOLUME;


// Get device Information handle for Volume interface
hDevInfo = SetupDiGetClassDevs(&guid, NULL, NULL,
DIGCF_DEVICEINTERFACE |
DIGCF_PRESENT);

if(hDevInfo == INVALID_HANDLE_VALUE)
{
WRITELOG("Error - SetupDiGetClassDevs failed\n");
return 0;
}

// Loop until device interfaces are found.
for(dwIndex = 0; ;dwIndex ++)
{
ZeroMemory(&devInterfaceData, sizeof(devInterfaceData));
devInterfaceData.cbSize = sizeof(devInterfaceData);

// Get device Interface data.

if(!SetupDiEnumDeviceInterfaces(hDevInfo, NULL, &guid,
dwIndex,&devInterfaceData))
{
break;
}

ZeroMemory(&devInfoData, sizeof(devInfoData));
devInfoData.cbSize = sizeof(devInfoData);

pDevDetail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)buffer;
pDevDetail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);

// Get device interface detail data to get
// Device Instance from SP_DEVINFO_DATA and
// Device Path from SP_DEVICE_INTERFACE_DETAIL_DATA

SetupDiGetDeviceInterfaceDetail(hDevInfo,
&devInterfaceData,
pDevDetail, // SP_DEVICE_INTERFACE_DETAIL_DATA
BUFFER_SIZE,
&dwRequiredSize,
&devInfoData); // SP_DEVINFO_DATA

// Get the device instance of parent. This points to USBSTOR.
CM_Get_Parent(&devInstParent,devInfoData.DevInst, 0);

// Get the device instance of grand parent. This points to USB root.
CM_Get_Parent(&devInstParent,devInstParent, 0);

// Get the device ID of the USB root.
CM_Get_Device_ID(devInstParent, buf, BUFFER_SIZE,0);

// If USB root device matches with the input device ID, it is the target
device.

if( buf != NULL && wcscmp(lpDevID,buf) == 0)
{
// Append \ to the DevicePath of SP_DEVICE_INTERFACE_DETAIL_DATA

nLength = wcslen(pDevDetail->DevicePath);
pDevDetail->DevicePath[nLength] = '\\';
pDevDetail->DevicePath[nLength+1] = 0;

// Get Volume mount point for the device path.
if(GetVolumeNameForVolumeMountPoint(pDevDetail->DevicePath, volume,
BUFFER_SIZE))
{
for(nLoopIndex=0; nLoopIndex< g_count; nLoopIndex++)
{
// Compare volume mount point with the one stored earlier.
// If both match, return the corresponding drive letter.

if(wcscmp(g_drives[nLoopIndex].volume, volume)==0)
{
SetupDiDestroyDeviceInfoList(hDevInfo);
return g_drives[nLoopIndex].letter;
}
}
}
}
}

SetupDiDestroyDeviceInfoList(hDevInfo);
WRITELOG("Error - No drives found in GetSpecificDrives\n");
return 0;
}
From: trejkaz@gmail.com on
Questions:
1. How big does BUFFER_SIZE have to be to fit the data which comes back
without throwing an error?
2. What is the value of DEVICE_ID?

From: Trejkaz on
Okay, to answer my own question, I don't need whatever DEVICE_ID is at
all, and I can use an enormous BUFFER_SIZE of 1024*4 to give way more
space than it needs.

The main problem with the above code is that it works backwards,
finding the drive letter for a device ID. I needed to find the device
ID for a drive letter, so my code is back to front compared to the code
above. Second, instead of going to the grandparent device ID, I had to
go only to the parent, because the IDs we were after were the device
instance IDs from the USBSTOR driver, not the USB driver.

After those fixes, it's all working, although I'm not completely
confident with my code at present (Java by trade, Ruby by nature) so I
won't post it up here (yet) in case it's really bad. :-)

Thanks.