(Windows 2000 及更高版本处理IRP_MN_QUERY_STOP_DEVICE请求)

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

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

  1. 确定是否可以停止设备,并释放其硬件资源,而不会造成负面影响。

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

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

    • 无法释放设备的硬件资源。

    如果以下情况成立,驱动程序可能会使查询停止 IRP 失败:

    • 驱动程序不得删除 I/O 请求,并且没有排队 IRP 的机制。

      当设备处于停止状态时,驱动程序必须保留需要访问设备的 IRP。 如果驱动程序未将 IRP 排队,则它不得允许设备停止,因此必须使查询停止 IRP 失败。

      此规则的例外是允许删除 I/O 的设备。 此类设备的驱动程序可以成功查询停止和停止请求,而无需排队 IRP。

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

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

  3. 如果设备可以停止并且驱动程序将 IRP 排队,请在设备扩展中设置HOLD_NEW_REQUESTS标志,以便后续 IRP 将排队 (请参阅在 设备暂停时保持传入 IRP) 。

    或者,设备的驱动程序可以完全暂停设备,直到驱动程序收到后续 IRP_MN_STOP_DEVICE 请求。 但是,此类驱动程序必须将任何请求排队,以防止他们在到达时立即接替停止 IRP。 在重启设备之前,此类驱动程序必须将请求排队,如下所示:

  4. 如果设备无法进行 IRP 失败,请确保传递给其他驱动程序例程和较低驱动程序的任何未完成请求都已完成。

    驱动程序可实现此目的的一种方法是使用引用计数和事件来确保所有请求都已完成:

    • 在其 AddDevice 例程中,驱动程序在设备扩展中定义 I/O 引用计数,并将计数初始化为 1。

    • 同样在其 AddDevice 例程中,驱动程序使用 KeInitializeEvent 创建事件,并使用 KeClearEvent 将该事件初始化为 Not-Signaled 状态。

    • 每次处理 IRP 时,驱动程序都会使用 InterlockedIncrement 递增引用计数。

    • 每次完成请求时,驱动程序都会使用 InterlockedDecrement 递减引用计数。

      如果请求有 IoCompletion 例程,驱动程序会递减 IoCompletion 例程中的引用计数;如果驱动程序未对请求使用 IoCompletion 例程,则驱动程序会立即在调用 IoCallDriver 之后递减引用计数。

    • 当驱动程序收到 IRP_MN_QUERY_STOP_DEVICE时,它会使用 InterlockedDecrement 递减引用计数。 如果没有未完成的请求,则会将引用计数减少到零。

    • 当引用计数达到零时,驱动程序使用 KeSetEvent 设置事件,指示查询停止代码可以继续。

    作为上述过程的替代方法,驱动程序可以序列化正在进行的任何 IRP 后面的 IRP_MN_QUERY_STOP_DEVICE IRP。

  5. 执行将设备置于停止挂起状态所需的任何其他步骤。

    驱动程序成功执行查询停止 IRP 后,必须准备好成功 IRP_MN_STOP_DEVICE

  6. 完成 IRP。

    在函数或筛选器驱动程序中:

    • Irp-IoStatus.Status> 设置为 STATUS_SUCCESS。

    • 使用 IoSkipCurrentIrpStackLocation 设置下一个堆栈位置,并使用 IoCallDriver 将 IRP 传递给下一个较低的驱动程序。

    • IoCallDriver 传播状态作为 DispatchPnP 例程的返回状态。

    • 请勿完成 IRP。

    在总线驱动程序中:

    • Irp-IoStatus.Status> 设置为 STATUS_SUCCESS。

      但是,如果总线上的设备使用硬件资源,请重新评估总线和子设备的资源要求。 如果任何要求已更改,则返回STATUS_RESOURCE_REQUIREMENTS_CHANGED而不是STATUS_SUCCESS。 此状态表示成功,但请求 PnP 管理器在发送停止 IRP 之前重新查询资源。

    • 使用 IO_NO_INCREMENT 完成 IRP (IoCompleteRequest) 。

    • DispatchPnP 例程返回。

如果设备堆栈中的任何驱动程序无法 IRP_MN_QUERY_STOP_DEVICE,PnP 管理器会将 IRP_MN_CANCEL_STOP_DEVICE 发送到设备堆栈。 这可以防止驱动程序要求查询停止 IRP 的 IoCompletion 例程来检测较低级别的驱动程序是否使 IRP 失败。