如何在Windows中以编程方式挂载驱动器?

4
我们制造和销售一种设备,有时用户会想要使用多个USB集线器连接到他们的计算机上。它是一个USB复合设备,具有人体接口(HID)和大容量存储(MSD)接口。Windows会自动挂载每个设备的文件系统,直到字母用尽为止,即“Z:”。

我可以遍历设备树,并使用PnP配置管理器设备安装函数的组合来获取HID和USBSTOR接口的设备实例标识符。通过USB存储设备路径,我还可以获取磁盘编号(即\\.\PhysicalDrive1)。

下一步将是通过循环驱动器字母来挂载这些磁盘,以便我们与设备通信,或者更好的方法是将它们挂载在C:驱动器的临时目录中。我尝试使用DefineDosDevice来完成此任务,但遇到了困难,并且无法使用SetVolumeMountPoint取得进展,因为设备在挂载之前没有卷 GUID。这会带来一个先有鸡还是先有蛋的问题。 如果我们的客户只使用Unix就好了!!!

好的,我现在尝试一下。 - Judge Maygarden
@Gabe,那么我如何将卷 GUID 与磁盘编号和/或设备 ID 匹配? - Judge Maygarden
你看过QueryDosDevice(http://msdn.microsoft.com/en-us/library/aa365461(VS.85).aspx)吗?我认为一个完整的示例可能会有所帮助:http://msdn.microsoft.com/en-us/library/cc542456(VS.85).aspx - Gabe
参见:http://stackoverflow.com/questions/3003810/how-to-get-the-volume-guid - Gabe
@Gabe 哇,这真让人沮丧!QueryDosDevice返回一个类似于\Device\HarddiskVolume21的字符串。我不确定如何构建可比较的字符串以匹配该字符串,因为我不知道那个21是如何生成的。那个对应于\\.\PhysicalDrive1... - Judge Maygarden
显示剩余5条评论
2个回答

9
Windows不会挂载磁盘,它会挂载卷。然而,USBSTOR类设备的卷在设备树中不被列为子节点。因此,您需要枚举所有卷,并进行一系列字符串操作和比较,以将STORAGE\VOLUME节点与USBSTOR节点匹配。
所有卷的GUID值都是使用FindFirstVolume函数集枚举的。可以去掉前导的"\.\"和尾随的"\"字符,然后将结果字符串传递给QueryDosDevice。这将提供一个设备名称。
接下来,您需要使用SetupDiGetClassDevs和相关工具使用GUID_DEVINTERFACE_VOLUME列举所有卷。使用IOCTL_STORAGE_GET_DEVICE_NUMBER比较每个卷的设备类型和数量与您要查找的USBSTOR设备。一旦匹配成功,您可以从卷中获取设备名称,并将其与其他设备名称列表进行比较,以查找卷GUID。
最后,可以使用SetVolumeMountPoint成功使用卷GUID。
感谢Gabe在我的问题评论中提供了非常有帮助的帮助。

代码片段

从设备路径获取设备类型和编号:

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);

通过迭代所有卷接口并比较上面片段中的磁盘编号,找到相应USBSTOR实例的设备名称。
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;
            }
        }
    }
}

0

我认为你需要使用IOCTL_MOUNTMGR_CREATE_POINT。不幸的是,大多数使用IOCTL_MOUNTMGR_XXX的示例都是针对内核模式驱动程序编写的,但这并非必需。也许我的旧答案(使用IOCTL_MOUNTMGR_QUERY_POINTS)和另一个答案可以帮助你完成这个任务。此外,还可以参考http://msdn.microsoft.com/en-us/library/ff567603.aspxhttp://support.microsoft.com/kb/836662

在更好地理解IOCTL_MOUNTMGR_CREATE_POINT的使用方法后,您可能能够解决与SetVolumeMountPoint相关的问题。


@Oleg,我发布了一个回答来解决自己的问题,这得益于@Gabe在评论中的帮助。我以前从未见过挂载管理器的方法,我会看一下它是否比多个设备枚举和字符串匹配更直接。 - Judge Maygarden
@Oleg,如果这浪费了您的时间,我很抱歉。Stack Overflow要等2天才能接受我的答案。 - Judge Maygarden
@Judge Maygarden:有趣!我以前不知道这个。stackoverflow上真的有一些奇怪的东西。 :-) 顺便说一下,如果你有时间,可以发布一下你使用的代码示例。我觉得你的问题很有趣。你是将USB存储挂载到文件夹还是驱动器的末尾? - Oleg
@Oleg 好的,我会处理。另外,我取消了接受我的答案,因为它不适用于Windows XP。所以,我仍在努力找到一种方法将USBSTOR设备节点与STORAGE/VOLUME设备节点关联起来,以便我可以获取卷GUID。 - Judge Maygarden
在Windows 7上,我使用我们的VID/PID找到每个USB设备的根节点,并遍历树以匹配HID和USBSTOR节点。然后,我枚举卷并尝试将它们与USBSTOR匹配。接下来,将每个匹配的卷挂载到目录中,这样我们就不会遇到驱动器字母用尽的问题。 - Judge Maygarden
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接