处理 IRP_MN_QUERY_REMOVE_DEVICE 请求

PnP 管理器发送此 IRP 以通知驱动程序设备即将从计算机中删除,并询问是否可以在不中断计算机的情况下删除该设备。 当用户请求更新设备的驱动程序时,它还会发送此 IRP。

PnP 管理器在 IRQL PASSIVE_LEVEL系统线程上下文中发送此 IRP。

在将此 IRP 发送到设备的驱动程序之前,它会执行以下操作:

  • 通知在设备上注册通知的所有用户模式应用程序 (或相关设备) 。

    这包括在设备上注册通知的应用程序、在设备的某个子代 (子设备、子级等) ,或设备删除关系之一上注册通知的应用程序。 应用程序通过调用 RegisterDeviceNotification 来注册此类通知。

    为了响应此通知,应用程序准备删除设备 (关闭设备) 句柄或查询失败。

  • 通知所有在设备上注册通知的内核模式驱动程序 (或相关设备) 。

    这包括在设备、设备后代之一或设备删除关系之一上注册通知的驱动程序。 驱动程序通过调用事件类别为 EventCategoryTargetDeviceChangeIoRegisterPlugPlayNotification 来注册此通知。

    为了响应此通知,驱动程序准备删除设备 (关闭设备) 句柄或查询失败。

  • IRP_MN_QUERY_REMOVE_DEVICE IRP 发送到设备后代的驱动程序。

  • (Windows 2000 及更高版本的系统) 如果在设备上装载文件系统,PnP 管理器会向文件系统和任何文件系统筛选器发送查询删除请求。 如果设备有打开的句柄,则文件系统通常会使查询删除请求失败。 如果没有,文件系统通常会锁定卷,以防止将来的创建成功。 如果装载的文件系统不支持查询删除请求,则 PnP 管理器将失败设备的查询删除请求。

如果上述所有步骤都成功,PnP 管理器会将 IRP_MN_QUERY_REMOVE_DEVICE 发送到设备的驱动程序。

IRP_MN_QUERY_REMOVE_DEVICE请求首先由设备堆栈中的顶部驱动程序处理,然后由下一个较低的驱动程序处理。 驱动程序在其 DispatchPnP 例程中处理删除 IRP。

为了响应 IRP_MN_QUERY_REMOVE_DEVICE,驱动程序必须执行以下操作:

  1. 确定是否可以在不中断操作的情况下从计算机中删除设备。

    如果满足以下任一条件,驱动程序必须使查询删除 IRP 失败:

    • 如果删除设备可能会导致数据丢失。

    • 如果组件具有打开的设备句柄。 (这是仅在 Windows 98/Me 上的问题。Windows 2000 及更高版本的 Windows 跟踪打开的句柄,如果 IRP_MN_QUERY_REMOVE_DEVICE 完成后有打开的句柄,则查询失败。)

    • 如果已通过 IRP_MN_DEVICE_USAGE_NOTIFICATION IRP (通知驱动程序,) 设备位于分页、故障转储或休眠文件的路径中。

    • 如果驱动程序具有针对设备的未完成接口引用。 也就是说,驱动程序提供了一个接口来响应 IRP_MN_QUERY_INTERFACE 请求,并且该接口尚未取消引用。

  2. 如果无法删除设备,则查询删除 IRP 失败。

    Irp-IoStatus.Status> 设置为适当的错误状态 (通常STATUS_UNSUCCESSFUL) ,使用 IO_NO_INCREMENT调用 IoCompleteRequest,然后从驱动程序的 DispatchPnP 例程返回。 不要将 IRP 传递给下一个较低的驱动程序。

  3. 如果驱动程序以前发送 IRP_MN_WAIT_WAKE 请求来启用设备唤醒,请取消等待唤醒 IRP。

  4. 记录设备的上一个 PnP 状态。

    驱动程序应记录驱动程序收到 IRP_MN_QUERY_REMOVE_DEVICE 请求时设备处于的 PnP 状态,因为如果取消查询 (IRP_MN_CANCEL_REMOVE_DEVICE ) ,驱动程序必须将设备返回到该状态。 以前的状态通常是“已启动”,即驱动程序成功完成 IRP_MN_START_DEVICE 请求时设备进入的状态。

    但是,其他以前的状态是可能的。 例如,用户可能已通过设备管理器禁用了设备。 或者,为了响应 IRP_MN_QUERY_CAPABILITIES 请求,父总线驱动程序 (或总线驱动程序上的筛选器驱动程序) 可能已报告设备的硬件已禁用。 在任一情况下,已禁用设备的驱动程序都可以在收到 IRP_MN_START_DEVICE 请求之前接收 IRP_MN_QUERY_REMOVE_DEVICE 请求。

  5. 完成 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 发送到设备堆栈。 这可以防止驱动程序要求查询删除 IRP 的 IoCompletion 例程来检测较低驱动程序是否使 IRP 失败。

驱动程序成功 IRP_MN_QUERY_REMOVE_DEVICE 并认为设备处于删除挂起状态后,驱动程序必须失败设备的任何后续创建请求。 驱动程序像往常一样处理所有其他 IRP,直到驱动程序收到 IRP_MN_CANCEL_REMOVE_DEVICEIRP_MN_REMOVE_DEVICE