Вот пример из DDK добавления IRP в очередь: Код (Text): NTSTATUS QueueIrp(DEVICE_CONTEXT *deviceContext, PIRP Irp) { PDRIVER_CANCEL oldCancelRoutine; KIRQL oldIrql; NTSTATUS status = STATUS_PENDING; KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql); // Queue the IRP and call IoMarkIrpPending // to indicate that the IRP may complete on a different thread. // N.B. It's okay to call these inside the spin lock because they're macros, not functions. IoMarkIrpPending(Irp); InsertTailList(&deviceContext->irpQueue, &Irp->Tail.Overlay.ListEntry); // Must set a Cancel routine before checking the Cancel flag. oldCancelRoutine = IoSetCancelRoutine(Irp, IrpCancelRoutine); ASSERT(!oldCancelRoutine); if (Irp->Cancel){ // The IRP was canceled. Check whether our cancel routine was called. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL); if (oldCancelRoutine){ // The cancel routine was NOT called. // So dequeue the IRP now and complete it after releasing the spin lock. RemoveEntryList(&Irp->Tail.Overlay.ListEntry); status = Irp->IoStatus.Status = STATUS_CANCELLED; } //endif else { // The cancel routine WAS called. // As soon as we drop our spin lock it will dequeue and complete the IRP. // So leave the IRP in the queue and otherwise don't touch it. // Return pending since we're not completing the IRP here. } //end else } // endif KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql); // Normally you shouldn't call IoMarkIrpPending // and return a status other than STATUS_PENDING. // But you can break this rule if you complete the IRP. if (status != STATUS_PENDING){ IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } Непонятно вот что: Код (Text): // Must set a Cancel routine before checking the Cancel flag. Почему нельзя сразу проверить флаг Irp->Cancel? И что отвечает за присвоение Irp->Cancel = TRUE, это делается автоматически или это необходимо явно прописать в CancelRoutine.
Вот код с моими комментариями, может кто скажет, где я ошибаюсь. Код (Text): NTSTATUS QueueIrp(DEVICE_CONTEXT *deviceContext, PIRP Irp) { PDRIVER_CANCEL oldCancelRoutine; KIRQL oldIrql; NTSTATUS status = STATUS_PENDING; // устанавливаем спин-блокировку KeAcquireSpinLock(&deviceContext->irpQueueSpinLock, &oldIrql); //указываем, что irp будет обработан позже IoMarkIrpPending(Irp); //добавляем в список irp InsertTailList(&deviceContext->irpQueue, &Irp->Tail.Overlay.ListEntry); //устанавливаем CancelRoutine для irp. ВАЖНО: я так понял, CancelRoutine для этого irp устанавливается в первый раз. oldCancelRoutine = IoSetCancelRoutine(Irp, IrpCancelRoutine); ASSERT(!oldCancelRoutine); //а вдруг до вызова QueueIrp(...) user-mode программа попыталась отменить свой запрос. Из кернел-мода был вызван IoCancelIrp(Irp), который вернул FALSE. Т.е. присвоил Irp->Cancel=TRUE, а CancelRoutine запустить не смог, ибо его установили только парой строчек выше. if (Irp->Cancel){ // Зачем нужна переменная oldCancelRoutine, я не знаю. Ибо в ней 100% будет назодится адрес IrpCancelRoutine. oldCancelRoutine = IoSetCancelRoutine(Irp, NULL); if (oldCancelRoutine){ // Будет выполняться только этот код. RemoveEntryList(&Irp->Tail.Overlay.ListEntry); status = Irp->IoStatus.Status = STATUS_CANCELLED; } //endif else { // Здесь ничего выполняться не будет. } //end else } // endif KeReleaseSpinLock(&deviceContext->irpQueueSpinLock, oldIrql); }
Установка рутины отмены и непосредственно отмена самого irp это разные вещи и не выполняются одновременно, это далается в разных частях кода в соответствии с алго. Если мутно объяснил то проще, кажется, так: Irp->Cancel означает что Irp _уже_ отменён, а Irp->CancelRoutine просто устанавливает рутину отмены.
Рекомендую прочитать в 5-ой главе книги Walter Oney - "Programming The Windows Driver Model 2nd Edition" разделы "Queuing I/O Requests" и "Cancelling I/O Requests". Там подробно отвечено на все вопросы. Есть переводное издание http://www.piter.com/book/978591180057/