如何从我的 Windows 驱动程序代码中区分 devmgmt 的禁用和卸载?
How to distinguish devmgmt's Disable and Uninstall from within my Windows driver code?
我正在研究来自 WDK7 的 Microsoft Toaster 示例代码,我发现了一个微妙的问题。
现在在 Windows 7 上尝试编译的驱动程序(WDM busenum 和 WDM featured1)。
按照 README 的指导,enum -p 1
添加了一个烤面包机设备,然后,我打开设备管理器 (devmgmt),找到该设备,卸载 它。
这将破坏烤面包机 devnode(我相信); 我们可以看到 ToasterDevice01 节点现在从设备管理器中消失了。 !devnode 0 1
表明 toaster devnode 仍然存在,State=DeviceNodeUninitialized (0x301),Previous State=DeviceNodeRemoved (0x312)。
然后,我执行enum -p 1
尝试再次添加设备。但是我收到错误 0x57(ERROR_INVALID_PARAMETER).
我调试了源码,查明原因:buspdo.c
没有区分devmgmt的Disable和Uninstall操作。他的代码逻辑是:
- 如果烤面包机获得 surprise-removed(
enum -u 1
),它会调用 Bus_DestroyPdo()
,这是正确的行为。
- 如果烤面包机从 devmgmt 被禁用,它 不会 调用
Bus_DestroyPdo()
,这也是正确的。
问题是,当end-user 从devmgmt 执行卸载时,它遵循禁用路径。现在发生了一些不好的事情:Windows 删除了烤面包机 devnode,但烤面包机总线驱动程序没有破坏相应的 PDO,因此,当下次用户执行 enum -p 1
时,烤面包机总线驱动程序 Bus_PlugInDevice()
指责SerialNo==1 的烤面包机设备已经存在,因此用户请求失败。
顺便说一句:Toaster 的 KMDF 版本出现了类似的问题(今天只试过 static-enumeration 版本)
现在我的问题很清楚了:如何区分禁用和卸载,我应该在总线驱动程序还是子设备驱动程序中进行?也欢迎 KMDF 版本的答案。
现在可以下结论了。我们的客户端驱动程序无法从设备管理器中区分禁用和卸载。这是微软的设计。
要进行区分,需要应用层的帮助。为 child 设备提供 CoInstaller,并且当 CoInstaller 收到 DIF_REMOVE 通知(这是 devmgmt 执行卸载的结果)时,向内核驱动程序发送自定义 IOCTL(例如 IOCTL_UNPLUG_MY_CHILD),以便该内核驱动程序拔出对应的child。
我正在研究来自 WDK7 的 Microsoft Toaster 示例代码,我发现了一个微妙的问题。
现在在 Windows 7 上尝试编译的驱动程序(WDM busenum 和 WDM featured1)。
按照 README 的指导,enum -p 1
添加了一个烤面包机设备,然后,我打开设备管理器 (devmgmt),找到该设备,卸载 它。
这将破坏烤面包机 devnode(我相信); 我们可以看到 ToasterDevice01 节点现在从设备管理器中消失了。 !devnode 0 1
表明 toaster devnode 仍然存在,State=DeviceNodeUninitialized (0x301),Previous State=DeviceNodeRemoved (0x312)。
然后,我执行enum -p 1
尝试再次添加设备。但是我收到错误 0x57(ERROR_INVALID_PARAMETER).
我调试了源码,查明原因:buspdo.c
没有区分devmgmt的Disable和Uninstall操作。他的代码逻辑是:
- 如果烤面包机获得 surprise-removed(
enum -u 1
),它会调用Bus_DestroyPdo()
,这是正确的行为。 - 如果烤面包机从 devmgmt 被禁用,它 不会 调用
Bus_DestroyPdo()
,这也是正确的。
问题是,当end-user 从devmgmt 执行卸载时,它遵循禁用路径。现在发生了一些不好的事情:Windows 删除了烤面包机 devnode,但烤面包机总线驱动程序没有破坏相应的 PDO,因此,当下次用户执行 enum -p 1
时,烤面包机总线驱动程序 Bus_PlugInDevice()
指责SerialNo==1 的烤面包机设备已经存在,因此用户请求失败。
顺便说一句:Toaster 的 KMDF 版本出现了类似的问题(今天只试过 static-enumeration 版本)
现在我的问题很清楚了:如何区分禁用和卸载,我应该在总线驱动程序还是子设备驱动程序中进行?也欢迎 KMDF 版本的答案。
现在可以下结论了。我们的客户端驱动程序无法从设备管理器中区分禁用和卸载。这是微软的设计。
要进行区分,需要应用层的帮助。为 child 设备提供 CoInstaller,并且当 CoInstaller 收到 DIF_REMOVE 通知(这是 devmgmt 执行卸载的结果)时,向内核驱动程序发送自定义 IOCTL(例如 IOCTL_UNPLUG_MY_CHILD),以便该内核驱动程序拔出对应的child。