实现 Cancel 例程
I/O 管理器调用驱动程序提供的 Cancel 例程,其中包含要取消的输入 IRP 和表示 I/O 请求的目标设备的 DeviceObject 指针。
IRP 可能是驱动程序的 DispatchReadWrite 例程在用户关闭当前 Win32 应用程序时已排队的 IRP。 IRP 也可能是更高级别驱动程序显式取消的 IRP,具体取决于基础设备的性质。
调用 Cancel 例程时,如果驱动程序具有 StartIo 例程,则输入 IRP 可能已经是目标设备对象中的 CurrentIrp,或者可能已在与目标设备对象关联的设备队列中。 如果驱动程序没有 StartIo 例程,则调用其 Cancel 例程时,IRP 可能位于驱动程序管理的 IRP 内部队列中。 在任何情况下,在 I/O 管理器为传入 IRP 调用 Cancel 例程之前,I/O 管理器会将此 IRP 中的 Cancel 成员设置为 TRUE ,并将 IRP 中的 CancelRoutine 成员设置为 NULL。
具有关联 IRP 的主 IRP 的 Cancel 例程负责调用 IoCancelIrp 来取消这些关联的 IRP。
所有 Cancel 例程必须遵循以下准则:
调用 IoReleaseCancelSpinLock 以释放系统的取消旋转锁。
将 I/O 状态块的 Status 成员设置为 STATUS_CANCELLED,并将其 信息 成员设置为零。
通过调用 IoCompleteRequest 完成指定的 IRP。
由于 始终调用 Cancel 例程并保留系统取消旋转锁,因此此例程不得调用 IoAcquireCancelSpinLock ,除非它先调用 IoReleaseCancelSpinLock 。
当系统返回控件时,Cancel 例程不能持有系统取消旋转锁。 也就是说,每个 Cancel 例程必须至少调用 IoReleaseCancelSpinLock 一次,然后才能返回控制权。
如果调用 IoAcquireCancelSpinLock, 则 Cancel 例程必须尽快对 IoReleaseCancelSpinLock 进行倒数调用。
切勿在按住旋转锁时使用 IRP 调用 IoCompleteRequest 。 尝试在按住旋转锁时完成 IRP 可能会导致死锁。