取消 I/O 请求
应用程序、系统或驱动程序可以取消设备正在进行的 I/O 操作 (例如从磁盘) 读取多个块的请求。 如果取消设备的 I/O 操作,I/O 管理器将尝试取消与 I/O 操作关联的所有未处理的 I/O 请求。 设备的驱动程序可以注册,以便在 I/O 管理器尝试取消 I/O 请求时收到通知,并且驱动程序可以通过以完成状态为STATUS_CANCELLED 来完成 它们来取消其拥有的请求。
框架处理基于框架的驱动程序的某些取消工作。 如果取消设备的 I/O 操作,框架将完成以下 I/O 请求, (完成状态为与取消的操作关联的STATUS_CANCELLED) :
框架已放入驱动程序的默认 I/O 队列中的未传递 I/O 请求。
由于驱动程序调用 了 WdfDeviceConfigureRequestDispatching,框架已转发到另一个队列的未传递 I/O 请求。
由于框架取消这些请求,因此不会将这些请求传递给驱动程序。
框架向驱动程序传递 I/O 请求后,驱动程序 拥有 该请求,并且框架无法取消该请求。 此时,只有驱动程序可以取消 I/O 请求,但框架必须通知驱动程序应取消请求。 驱动程序通过提供 EvtRequestCancel 回调函数接收此通知。
有时驱动程序从 I/O 队列接收 I/O 请求,但驱动程序会将请求重新排到同一个或其他 I/O 队列,以便以后处理,而不是处理该请求。 这种情况的示例包括:
框架将 I/O 请求传递到驱动程序的请求 处理程序之一,驱动程序随后调用 WdfRequestForwardToIoQueue (或 WdfRequestForwardToParentDeviceIoQueue) 将请求放入其他队列或 WdfRequestRequeue 以将请求放回到同一队列中。
框架向驱动程序的 EvtIoInCallerContext 回调函数传递 I/O 请求,驱动程序调用 WdfDeviceEnqueueRequest 将请求传递回框架,框架随后将请求置于驱动程序的 I/O 队列之一中。
在这些情况下,框架可以取消 I/O 请求,因为请求位于 I/O 队列中。 但是,如果驱动程序已为请求所在的 I/O 队列注册了 EvtIoCanceledOnQueue 回调函数,则框架会在取消关联的 I/O 操作时调用回调函数,而不是取消请求。 如果框架调用驱动程序的 EvtIoCanceledOnQueue 回调函数,则驱动程序必须 完成 请求。
总之,取消 I/O 操作时,框架始终取消从未传递到驱动程序的所有关联 I/O 请求。 如果驱动程序收到请求,然后重新排队,框架将取消请求 (如果请求位于队列) ,除非驱动程序为 I/O 队列提供 EvtIoCanceledOnQueue 回调函数。
调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx
驱动程序可以调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx 来注册 EvtRequestCancel 回调函数。 如果驱动程序已调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx,并且取消了与请求关联的 I/O 操作,框架将调用驱动程序的 EvtRequestCancel 回调函数,以便驱动程序可以取消 I/O 请求。
驱动程序应调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx (如果它将拥有相对较长的一段时间的请求)。 例如,驱动程序可能必须等待设备响应,或者可能等待较低的驱动程序完成驱动程序在收到单个请求时创建的一组请求。
如果驱动程序未调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx,或者驱动程序在调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx 后调用 WdfRequestUnmarkCancelable,则驱动程序不知道取消,因此会像平时一样处理请求。
调用 WdfRequestIsCanceled
如果驱动程序未调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx 来注册 EvtRequestCancel 回调函数,它可以调用 WdfRequestIsCanceled 来确定 I/O 管理器是否已尝试取消 I/O 请求。 如果 WdfRequestIsCanceled 返回 TRUE 并且驱动程序拥有该请求,则驱动程序应取消该请求。 如果驱动程序不拥有请求,则不应调用 WdfRequestIsCanceled。
在以下情况下,未调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx 的驱动程序可能会调用 WdfRequestIsCanceled :
等待设备中断的驱动程序可能会从其 EvtInterruptDpc 回调函数调用 WdfRequestIsCanceled。
轮询其设备的驱动程序可能会从其轮询线程调用 WdfRequestIsCanceled 。
将 DMA 事务 分解为多个较小传输的驱动程序可能会在每次传输完成后调用 WdfRequestIsCanceled 。
如果驱动程序尚未为收到的请求调用 WdfRequestMarkCancelable 或 WdfRequestMarkCancelableEx,则接收大型读取或写入请求的驱动程序可能会在驱动程序的 I/O 目标完成每个较小的请求后调用 WdfRequestIsCanceled。
取消请求
取消 I/O 请求可能涉及以下任何一项:
停止正在进行的 I/O 操作。
不将请求转发到 I/O 目标。
调用 WdfRequestCancelSentRequest 以尝试取消驱动程序之前已提交到 I/O 目标的请求。
如果驱动程序取消该驱动程序从框架接收的请求对象的 I/O 请求,则驱动程序必须始终通过调用 WdfRequestComplete、 WdfRequestCompleteWithInformation 或 WdfRequestCompleteWithPriorityBoost 来完成请求,并且 状态 参数为 STATUS_CANCELLED。 (如果驱动程序调用 WdfRequestCreate 来创建请求对象,驱动程序将调用 WdfObjectDelete 而不是完成 request.)
同步取消
有关同步取消 I/O 请求的代码的信息,请参阅: