处理 IRP_MN_QUERY_REMOVE_DEVICE 请求

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

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

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

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

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

    为了响应此通知,应用程序要么准备删除设备(关闭设备句柄),要么查询失败。 有关如何处理这些通知的详细信息,请参阅注册获取设备接口到达和设备删除通知

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

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

    为了响应此通知,驱动程序要么准备移除设备(如关闭设备句柄),要么拒绝查询。

  • 向设备子代的驱动程序发送 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时,驱动程序必须执行以下操作:

  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 请求时输入的状态。

    然而,其他先前的状态也是可能的。 例如,用户可能已通过 Device Manager 禁用了设备。 或者,为了响应 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 到设备堆栈。 这可以防止驱动程序需要 IoCompletion 例程来检测较低的驱动程序是否未能通过 IRP。

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