直接上代码。
第一种:
NTSTATUS DispatchSomething(PDEVICE_OBJECT fidp, PIRP irp)
{
    PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fido->DevcieExtension;
    IoCopyCurrentIrpStackLocationToNext(irp);
    IoSetCompletionRoutine(irp, CompletionRoutine, ...);
    NTSTATUS status = IoCallDriver(pdx->LowerDevice, irp);
    if (STATUS_PENDING == status)
    {
        IoMarkIrpPending(Irp)
    }
    return status;
}
这种情况貌似很正确,当下层返回STATUS_PENDING的时候标记一下
其实错就错在当返回STATUS_PENDING的时候这个IRP可能已经完成了,这时候千万不要碰IRP了。


第二种:
NTSTATUS DispatchSomething(PDEVICE_OBJECT fido, PIRP irp)
{
    PDEVICE_EXTENSION pdx = fido->DeviceExtension;
    IoMarkIrpPending(irp);
    IoCopyCurrrentIrpStackLocationToNext(irp);
    IoSetCompletionRoutine(irp, TopDriverCompleteRoutine, ...);
    return IoCallDriver(pdx->LowrDevice, irp);
}
同样,看起来没什么问题,大不了我先标记一下吧。
这里的问题在于IoMarkIrpPending和STATUS_PENDING必须配对使用,这里是标记了要Pending,但是函数却有可能返回了别的值。

第三种:
NTSTATUS DeispatchSomething(PDEVCIE_OBJECT fido, PIRP irp)
{
    PDEVICE_EXTENSION pdx = fido->DeviceExtenson;
    KEVENT     event;
    KeInitializeEvent(&event, NotificaionEvent, FALSE);
    IoCopyCurrentIrpStackLocationToNext(irp);
    IoSetCompletionRoutine(irp, CompletionRoutine, &event, TRUE,TRUE,TRUE);
    IoCallDriver(pdx->LowerDeviceObject, irp);
    KeWaitForSingleObject(&event, ...);
    Irp->IoStatus.Status = status;
    IoCompleteRequest(irp, IO_NO_INCREMENT);
    return status;
}
NTSTATUS CompletionRoutine(PDEVICE_OBJECT fido, PIRP irp, PVOID pev)
{
    if (Irp->PendingReturned)
    {
        IoMarkIrpPending(irp);
    }
    KeSetEvent((PKEVENT)pev, IO_NO_CREMENT, FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
}
这个是在完成例程中标记的,如果下层异步完成,则标记,看起来没错,其实也犯了和前一个一样的错误。


第四种:
VOID SomeFunction()
{
    PIRP irp = IoBuildAsynchronousFsdRequest(...);
    IoSetCompleteRoutine(irp, MyCompleteRoutine, ...);
    IoCallDriver(...);
}
NTSTATUS MyCompletionRoutine(PDEVCIE_OBJECT junk, PIRP irp, PVOID context)
{
    if (Irp->PendingReturned)
    {
        IoMarkIrpPending(irp);
    }
    IoFreeIrp(irp);
    return STATUS_MORE_PROCESISING_REQUIRED;
}
这种方式为自己构造IRP,然后传递给下层处理。
该情况的错误在于自己构造的IRP没有当前栈,所以也无法调用IoMarkIrpPending


在实际代码编写中一定要注意避免上述几种错误