4

We make and sell a device that our users will sometimes want to connect to their computer in large quantities with multiple USB hubs. It is a USB composite device that has both human interface (HID) and mass storage (MSD) interfaces. Windows automatically mounts the file-system of each device until it runs out of letters at 'Z:'.

I can walk the device tree and get the device instance identifiers for the HID and USBSTOR interfaces using a combination of the PnP Configuration Manager and Device Installation functions. With the USB storage device path I can also get the disk number (i.e. \\.\PhysicalDrive1).

The next step would be to mount these disks as need by cycling out drive letters as we communicate with the devices, or better yet, mount them in temporary directories on the C: drive. I'm having difficulties attempting to use DefineDosDevice for this task and cannot make headway with SetVolumeMountPoint since a device does not have a Volume GUID until it is mounted. That presents a chicken and egg problem.

If only our customers used unix!!!

Judge Maygarden
  • 26,961
  • 9
  • 82
  • 99
  • When you say "device does not have a Volume GUID until it is mounted", do you mean that it doesn't have one, or that you don't know what it is? `FindFirstVolume` (http://msdn.microsoft.com/en-us/library/aa364425(VS.85).aspx) should let you enumerate even unmounted volumes. – Gabe Sep 24 '10 at 14:40
  • OK, I'm giving that a go right now. – Judge Maygarden Sep 24 '10 at 14:45
  • @Gabe So, how do I match the volume GUID to the disk number and/or device ID? – Judge Maygarden Sep 24 '10 at 16:06
  • Have you looked at QueryDosDevice (http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx)? I think maybe a complete sample will help: http://msdn.microsoft.com/en-us/library/cc542456(VS.85).aspx – Gabe Sep 24 '10 at 16:36
  • See also: http://stackoverflow.com/questions/3003810/how-to-get-the-volume-guid – Gabe Sep 24 '10 at 16:42
  • @Gabe Wow this is frustrating! QueryDosDevice returns a string like `\Device\HarddiskVolume21`. I'm not sure how to build a comparable string to match against since I don't know how that 21 is generated. That one corresponds to `\\.\PhysicalDrive1`... – Judge Maygarden Sep 24 '10 at 16:53
  • The problem is that you can't mount a drive; you can only mount a volume. You need to enumerate the children of the USBSTOR to get the actual volumes on a drive. – Gabe Sep 24 '10 at 17:12
  • @Gabe I was just trying to do that with CM_Get_Child and CM_Get_Sibling, but my USBSTOR nodes don't have any children. This is the case both when the drive is mounted (i.e. has a drive letter) and when it is not mounted. – Judge Maygarden Sep 24 '10 at 18:40
  • @Gabe However, I can enumerate GUID_DEVINTERFACE_VOLUME and find them... I wonder why they aren't children of the USBSTOR instance? – Judge Maygarden Sep 24 '10 at 18:49
  • The volumes are not children of the USB device. For example: \Device\HarddiskVolume25 => ROOT\VOLMGR\0000 => HTREE\ROOT\0 – Judge Maygarden Sep 24 '10 at 18:59

2 Answers2

9

Windows does not mount disks; it mounts volumes. However, the volume for a USBSTOR class device is not listed as a child node in the device tree. Therefore, you have to enumerate all volumes and and do a bunch of string manipulation and comparisons to match up STORAGE\VOLUME nodes with USBSTOR nodes.

All volume GUID values are enumerated with the FindFirstVolume set of functions. The leading "\.\" and trailing "\" characters can be stripped and the resulting string then passed to QueryDosDevice. This provides a device name.

Next, one must enumerate all volumes using GUID_DEVINTERFACE_VOLUME with SetupDiGetClassDevs and friends. Compare the device type and number of each volume to the USBSTOR device you are looking for using IOCTL_STORAGE_GET_DEVICE_NUMBER. Once those are matched, you can get the device name from the volume and compare that to the other list of device names to find the volume GUID.

Finally, the volume GUID can be successfully used with SetVolumeMountPoint.

Thanks to Gabe for his very helpful assistance in the comments to my question.


Code Snippets

Get device type and number from device path:

STORAGE_DEVICE_NUMBER sdn;
HANDLE handle = CreateFile(devInterfaceDetail->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, NULL);
DWORD len = 0;
DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0, &sdn, sizeof (sdn), &len, NULL);

Find the device name for the corresponding USBSTOR instance by iterating over all volume interfaces and comparing the disk number from the above snippet:

std::string deviceName;
HDEVINFO devInfoSet = SetupDiGetClassDevs(&GUID_DEVINTERFACE_VOLUME, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
SP_DEVICE_INTERFACE_DATA devInterface = { 0 };
devInterface.cbSize = sizeof (SP_DEVICE_INTERFACE_DATA);
for (int i = 0; SetupDiEnumDeviceInterfaces(devInfoSet, NULL, &GUID_DEVINTERFACE_VOLUME, i, &devInterface); ++i) {
    SP_DEVINFO_DATA devInfoData = { 0 };
    devInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
    DWORD len;
    SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterface, NULL, 0, &len, &devInfoData);
    std::vector<char> buf(len);
    SP_DEVICE_INTERFACE_DETAIL_DATA *devInterfaceDetail = (SP_DEVICE_INTERFACE_DETAIL_DATA *) &buf[0];
    devInterfaceDetail->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
    if (SetupDiGetDeviceInterfaceDetail(devInfoSet, &devInterface, devInterfaceDetail, len, NULL, &devInfoData)) {
        if (DEVICE_NUMBER == this->getDeviceNumber(devInterfaceDetail->DevicePath)) {
            std::vector<BYTE> buf(MAX_PATH + 1);
            DWORD type, len;
            if (SetupDiGetDeviceRegistryProperty(devInfoSet, &devInfoData, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, &type, &buf[0], buf.size(), &len)) {
                deviceName.assign(buf.begin(), buf.begin() + len);
                break;
            }
        }
    }
}
Community
  • 1
  • 1
Judge Maygarden
  • 26,961
  • 9
  • 82
  • 99
0

It seems to me that you have to use IOCTL_MOUNTMGR_CREATE_POINT. Unfortunately the most example used IOCTL_MOUNTMGR_XXX are written for kernel mode drivers, but it is not required. Probably my old answer (which use IOCTL_MOUNTMGR_QUERY_POINTS) and another one can help you to do this. See also http://msdn.microsoft.com/en-us/library/ff567603.aspx and http://support.microsoft.com/kb/836662.

It can be that after better understanding how IOCTL_MOUNTMGR_CREATE_POINT should be used you will be able to solve the problem with respect of SetVolumeMountPoint.

Community
  • 1
  • 1
Oleg
  • 220,925
  • 34
  • 403
  • 798
  • @Oleg I posted an answer to my own question that I worked out thanks to @Gabe's help in the comments. I'd never seen the mount manager approach and will take a look at it to see if it is more direct than several device enumerations and string matching. – Judge Maygarden Sep 27 '10 at 13:39
  • @Oleg I'm sorry if this wasted your time. Stack Overflow wouldn't allow me to except my own answer until 2 days had passed. – Judge Maygarden Sep 27 '10 at 14:39
  • @Judge Maygarden: Interesting! I don't known this before. There are really some strange things on the stackoverflow. :-) By the way if you sometime find the time you could post the code example which you use. I find your question interesting. Do you mount at the end the USB Storage to a folder or to a drive? – Oleg Sep 27 '10 at 14:52
  • @Oleg Will do. Also, I'm un-accepting my answer because it doesn't work for Windows XP. So, I'm still trying to find a way to associate a USBSTOR device node with a STORAGE/VOLUME device node so that I can get the volume GUID. – Judge Maygarden Sep 27 '10 at 16:17
  • @Oleg On Windows 7 I find the root node for each USB device with our VID/PID and traverse the tree to match up the HID and USBSTOR nodes. Then I enumerate the volumes and try to match them to the USBSTOR. Next, each matching volume is mounted to a directory so we don't have problems running out of drive letters. – Judge Maygarden Sep 27 '10 at 16:19
  • @Judge Maygarden: Try with http://stackoverflow.com/questions/3098696/same-code-returns-diffrent-result-on-windows7-32-bit-system/3100268#3100268. I posted the link before. It has link http://www.ok-soft-gmbh.com/ForStackOverflow/EnumMassStorage.c to the full code which is also tested on Windows XP. The program do all what you describes and a little more, but of cause **without** usage of `SetVolumeMountPoint`. Probably you can modify my program and include the part with the volume mounting inside? – Oleg Sep 27 '10 at 16:38
  • @Oleg I'm now matching the device type and device number instead of sub-string matching with the device instance ID. It is working like a champ. I'll update my answer shortly. – Judge Maygarden Sep 27 '10 at 19:23