【Win7】弹出U盘
概述:Win7 弹出U盘的方式汇总
参考文章:
- How to Prepare a USB Drive for Safe Removal - CodeProject
参考代码: - balena-io-modules/mountutils: Cross platform mount related utilities
- imdisk/cli/imdisk.c at 3f4672f83f02d18230bc1d47eb4617d106cc922c · dindinw/imdisk
问题描述
用户环境:Windows 7 x64 旗舰版
客户反馈使用 [U盘小助手] 弹出U盘时报错。弹出失败,每次都需要强制弹出才能弹出U盘,且强制弹出后 [Explorer] 界面仍会显示 U盘 对应的磁盘(没有容量)。但是使用系统的弹出是可以弹出的,且U盘图标弹出逻辑也正常。
异常情况就是上述两者发生:
排查思路
句柄未关闭
排查是否有未关闭的句柄导致弹出失败。具体就是使用 [Process Explorer] 查找 [U盘] 句柄。
被拦截
- 查看系统日志
- 查看 API 调用
查看代码
如果以上均排查不出来问题,那就只能看是不是代码写的有问题了。
以上只是常见的排查思路,具体问题具体分析。
弹出调用失败
代码中调用弹出的接口是 CM_Request_Device_Eject_Ex
问题原因
驱动问题,调用 CM_Request_Device_Eject_Ex
弹出时 pVetoType
返回 PNP_VetoIllegalDeviceRequest
,表示当前设备不支持。这个在别的机器上是无法复现,截止目前我也不知道这个问题的根源是什么。只能通过别的方式弹出。
目前看只有 CM_Request_Device_Eject_Ex
才能从设备管理器界面弹出U盘。
1 |
|
当 CM_Request_Device_Eject_Ex
接口不支持调用时,常见的方式就是调用 DeviceIOControl
来强制弹出U盘。示例代码如下所示:
1 |
|
在大多是机器都是正常的,但是在该客户的机器,调用上述代码后,虽然U盘设备已经移除,但是explorer 仍会有U盘图标显示。对此,我的解决方案就是刷新一下 explorer 界面,让已弹出的U盘不显示。
解决思路
总共试了以下多个 API 尝试通知到 explorer。
API 1 DeleteVolumeMountPointW (不要用)
由于尝试了多个 API 无果后,就尝试了调用了 以下接口,导致了后续一系列问题,在此避坑,建议了解当前函数的使用场景后再决定是否调用。
1 |
|
API 2 DefineDosDevice
DefineDosDeviceA 函数 (winbase.h) - Win32 apps | Microsoft Learn
DefineDosDevice 会删除驱动器号。但是在。
1 |
|
API 3 SendMessage
通过 SendMessage
发送 WM_DEVICECHANGE
消息给所有窗口。
1 |
|
API 4 RunDLL32.EXE
调用 RunDLL32.EXE
来实现弹出。以下两种方法均不可行,有需求可以深入研究下。
1 |
|
API 5 重新挂载达到刷新目的
结果:同样不生效,U盘设备移除,但是 Expolorer 图标仍在。
1 |
|
API 5 BroadcastSystemMessage
调用 BroadcastSystemMessage
广播通知设备改动
结果:不生效,图标在、设备也在
1 |
|
API 6 刷新设备列表
调用 SetupDI*
接口刷新设备列表。
结果:无效。因为设备并没有实际弹出,所有刷新后设备列表还是会有便携设备存在。
1 |
|
API 7 刷新 Explorer 界面
1 |
|
最终解决方案
调用 DeivceIOControl
强制弹出,然后通知界面刷新。
界面刷新调用了 SendMessage
和 SHChangeNotify
,代码如下所示:
1 |
|
事实上,只要 CM_Request_Device_Eject_Ex
调用成功,那图标缓存和设备列表都会更新,但是调用失败的时候就比较复杂,尤其是返回 PNP_VetoIllegalDeviceRequest
时,没有合适的方法能从设备列表移除设备。
备忘
1 |
|