处理 IRP_MN_QUERY_REMOVE_DEVICE 请求
PnP 管理器发送此 IRP,通知设备驱动程序某个设备即将被移除,并询问是否可以在不中断计算机的情况下移除该设备。 当用户请求更新设备的驱动程序时,它还会发送此 IRP。
PnP 管理器在系统线程的上下文中,在 IRQL PASSIVE_LEVEL 时发送此 IRP。
在将此 IRP 发送给设备的驱动程序之前,它会执行以下操作:
通知在设备(或相关设备)上注册通知的所有用户模式应用程序。
这包括在设备、设备的一个子设备(子设备、子设备的子设备等)或设备的一个删除关系上注册通知的应用程序。 应用程序通过调用 CM_Register_Notification 或 RegisterDeviceNotification注册此类通知。
为了响应此通知,应用程序要么准备删除设备(关闭设备句柄),要么查询失败。 有关如何处理这些通知的详细信息,请参阅注册获取设备接口到达和设备删除通知。
通知在设备(或相关设备)上注册通知的所有内核模式驱动程序。
这包括在设备、设备的一个子代或设备的一个删除关系上注册通知的驱动程序。 驱动程序通过调用事件类别为 EventCategoryTargetDeviceChange 的 IoRegisterPlugPlayNotification 来注册此通知。
为了响应此通知,驱动程序要么准备移除设备(如关闭设备句柄),要么拒绝查询。
向设备子代的驱动程序发送 IRP_MN_QUERY_REMOVE_DEVICE IRP。 有关设备堆栈如何处理此 IRP 的详细信息,请参阅下文。
(Windows 2000 及更高版本系统)如果设备上装载了文件系统,PnP 管理器会将查询删除请求发送到文件系统和任何文件系统筛选器。 如果设备有打开的句柄,文件系统通常会使查询删除请求失败。 否则,文件系统通常会锁定卷,以防止以后的创建成功。 如果装载的文件系统不支持查询移除请求,PnP 管理器将导致设备的查询移除请求失败。
如果上述所有步骤都成功,PnP 管理器会将 IRP_MN_QUERY_REMOVE_DEVICE 发送到设备的驱动程序。
IRP_MN_QUERY_REMOVE_DEVICE 请求首先由设备堆栈中的顶级驱动程序处理,然后由每一个更低层级的驱动程序依次处理。 驱动程序在其 DispatchPnP 例程中处理删除 IRP。
响应 IRP_MN_QUERY_REMOVE_DEVICE时,驱动程序必须执行以下操作:
确定是否可以安全地从计算机中删除设备。
如果以下任一情况成立,则驱动程序必须终止查询删除 IRP:
如果删除设备可能会导致数据丢失。
如果组件对设备有一个打开的句柄。 (仅 Windows 98/Me 上存在此问题。Windows 2000 及更高版本的 Windows 会跟踪被打开的句柄,如果在 IRP_MN_QUERY_REMOVE_DEVICE 完成后仍有被打开的句柄,查询将失败。)
如果驱动程序已收到通知(通过 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP),表明该设备位于分页、崩溃转储或休眠文件的路径中。
如果驱动程序对设备有未完成的接口引用。 也就是说,驱动程序提供了一个接口来响应 IRP_MN_QUERY_INTERFACE 请求,并且该接口尚未被取消引用。
如果无法删除设备,则查询删除 IRP 失败。
将 Irp->IoStatus.Status 设置为适当的错误状态(通常为 STATUS_UNSUCCESSFUL),使用 IO_NO_INCREMENT 调用 IoCompleteRequest,并从驱动程序的 dispatchPnP 例程返回。 不要将 IRP 传递到下一个较低驱动程序。
如果驱动程序之前发送了一个 IRP_MN_WAIT_WAKE 请求来启用设备唤醒,请取消等待唤醒 IRP。
记录设备的上一个 PnP 状态。
驱动程序应记录驱动程序收到 IRP_MN_QUERY_REMOVE_DEVICE 请求时设备处于的 PnP 状态,因为驱动程序必须在取消查询(IRP_MN_CANCEL_REMOVE_DEVICE)时将设备返回到该状态。 以前的状态通常为“已启动”,这是设备在驱动程序成功完成 IRP_MN_START_DEVICE 请求时输入的状态。
然而,其他先前的状态也是可能的。 例如,用户可能已通过 Device Manager 禁用了设备。 或者,为了响应 IRP_MN_QUERY_CAPABILITIES 请求,父总线驱动程序(或总线驱动程序上的筛选器驱动程序)可能已报告设备的硬件已禁用。 在任一情况下,禁用设备的驱动程序都可以在收到 IRP_MN_START_DEVICE 请求之前接收 IRP_MN_QUERY_REMOVE_DEVICE 请求。
完成 IRP:
在函数或筛选器驱动程序中:
将 Irp->IoStatus.Status 设置为STATUS_SUCCESS。
使用 IoSkipCurrentIrpStackLocation 设置下一个堆栈位置,并使用 IoCallDriver将 IRP 传递给下一个较低驱动程序。
将 IoCallDriver 的状态传播为 DispatchPnP 例程的返回状态。
不要完成 IRP。
在总线驱动程序中:
将 Irp->IoStatus.Status 设置为STATUS_SUCCESS。
使用 IO_NO_INCREMENT 完成 IRP (IoCompleteRequest)。
从 DispatchPnP 例程返回。
如果设备堆栈中的任何驱动程序在处理 IRP_MN_QUERY_REMOVE_DEVICE请求时失败,PnP 管理器将发送 IRP_MN_CANCEL_REMOVE_DEVICE 到设备堆栈。 这可以防止驱动程序需要 IoCompletion 例程来检测较低的驱动程序是否未能通过 IRP。
一旦驱动程序成功执行 IRP_MN_QUERY_REMOVE_DEVICE,并且认为设备处于删除挂起状态,则驱动程序必须使设备的任何后续创建请求失败。 驱动程序像往常一样处理所有其他 IRP,直到驱动程序收到 IRP_MN_CANCEL_REMOVE_DEVICE 或 IRP_MN_REMOVE_DEVICE。