自定义IRP的完成与处理
一般情况下,当我们在windows驱动中收到IRP时,驱动根据实际情况分为几种情况:
第一种是直接完成填充相关的参数后,直接调用IoCompleteRequest完成。
第二种情况是该层做相关的设置,如设置完成例程,然后调用IoCallDriver进行下发。
在第二种情况下,一般是当在IRP_MN_START时,我们设置IoSetCompletionRoutine,并下发:
NTSTATUS
SyncForwardIrp(
PDEVICE_OBJECT DeviceObject,
PIRP Irp)
{
NTSTATUS status = STATUS_SUCCESS;
KEVENT event;
KeInitializeEvent(&event, NotificationEvent, FALSE);
IoCopyCurrentIrpStackLocationToNext(Irp);
IoSetCompletionRoutine(Irp,
(PIO_COMPLETION_ROUTINE)IrpCompletionRoutine,
(PVOID) & event,
TRUE,
TRUE,
TRUE);
status = IoCallDriver(DeviceObject, Irp);
if (status == STATUS_PENDING)
{
KeWaitForSingleObject(&event,
Executive,
KernelMode,
FALSE,
NULL);
status = Irp->IoStatus.Status;
}
return status;
}
这里我们等待下层的驱动完成该IRP时,调用我们的回调函数,我们通过返回STATUS_MORE_PROCESSING_REQUIRED重新获取IPR的使用权,当然有的时候我们也会返回STATUS_CONTINUE_COMPLETION继续完成IRP.
这种情况一般都是我们最常见的IRP派遣与完成。
但有的时候,我们的需求变化了,就是当我们收到上层下发的IRP时,需要将该IRP pending,需要重新生成新的IRP,然后再下发给底层驱动,等待其完成,这时,它的完成函数该怎么处理呢?
- 使用IoAllocateIrp创建IRP.
- 使用IoGetNextIrpStackLocation获取下层的堆栈,并设置对应的IRP参数。
- 使用 IoSetCompletionRoutine 加入下层设备完成该IRP时的回调函数。
- 使用IoCallDriver下发新创建的IRP到底层设备
我们的初想是当下层完成IRP时,我们只在完成函数中获取相关的数据即可,这时可以返回STATUS_CONTINUE_COMPLETION。不过当设置该标志时,系统会蓝屏。
这是什么原因了?
这主要的原因是我们未设置当前StackLocation相关的参数所致,所以肯定是蓝到IoCompleteRequest中了。所以如果想要处理这类问题,就必须设置为STATUS_MORE_PROCESSING_REQUIRED,重新获取IRP权限,使IoCompleteRequest中断,这样就从下层的StackLocation中直接返回即可。
然后这时可以通过释放该IRP创建的其它资源和调用IoFreeIrp释放IRP即可。
如我们以下发一个USB中断请求为例:
NewIrp =IoAllocateIrp(DeviceObject->StackSize, FALSE);
...
NewIoStack = IoGetNextIrpStackLocation(NewIrp);
NewIoStack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NewIoStack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
NewIoStack->Parameters.Others.Argument1 = NewUrb;
...
IoSetCompletionRoutine(NewIrp, NewIrpCompleteRoutine, Context, TRUE, TRUE, TRUE);
status = IoCallDriver(FDODeviceExtension->NextDeviceObject, NewIrp);
...
}
而完成例程:
NTSTATUS
NewIrpCompleteRoutine(
PDEVICE_OBJECT DeviceObject,
PIRP Irp,
PVOID Context
)
{
...
ExFreePool(urb);
IoFreeIrp(Irp);
return STATUS_MORE_PROCESSING_REQUIRED;
}