简介
Windows 驱动程序模型 (WDM) 驱动程序中最常用完成的任务之一将输入/输出请求数据包 (Irp) 从一个驱动程序发送到另一个驱动程序中。 驱动程序将创建其自己的 IRP,并将其发送到一个较低的驱动程序,或者驱动程序会将它从上面附加的另一个驱动程序接收 Irp 转发。本文讨论了驱动程序可以发送 Irp 到带批注的示例代码使用较低的驱动程序的所有可能的方式。 根据需要,驱动程序编写人员可以按照本文中提供的模板之一,而且不会受旧 IRP 处理规则。
显示有关如何将 IRP 转发给另一个驱动程序,从一个调度例程 5 方案,这一主题的第 1 部分,并 (在此主题中的第 2 部分中列出) 的剩余 7 方案讨论不同的方法来创建 IRP,并将其发送到另一个驱动程序。 这一主题的第二部分包含在以下知识库文章:
326315
不同的方法来处理 Irp 的便笺 (2 的第 2 部分)
检查各种方案之前,请注意下列有关完成例程将返回的状态:
IRP 完成例程可以返回 STATUS_MORE_PROCESSING_REQUIRED 或 STATUS_SUCCESS。
它会检查状态时,I/O 管理器使用下面的规则:
- 如果状态是 STATUS_MORE_PROCESSING_REQUIRED,完成 IRP,停止将保留不变的堆栈位置并返回。
- 如果状态是 STATUS_MORE_PROCESSING_REQUIRED 以外的任何值,继续完成 IRP 向上。
以提高代码、 Windows XP SP1 和 Windows XP.NET 驱动程序开发工具包 Ntddk.h 和 Wdm.h 头文件的可读性将具有新#define名为 STATUS_CONTINUE_COMPLETION,这是化名为 STATUS_SUCCESS,如下面的代码所示:
// // This value should be returned from completion routines to continue // completing the IRP upwards. Otherwise, STATUS_MORE_PROCESSING_REQUIRED // should be returned. // #define STATUS_CONTINUE_COMPLETION STATUS_SUCCESS // // Completion routines can also use this enumeration instead of status codes. // typedef enum _IO_COMPLETION_ROUTINE_RESULT { ContinueCompletion = STATUS_CONTINUE_COMPLETION, StopCompletion = STATUS_MORE_PROCESSING_REQUIRED } IO_COMPLETION_ROUTINE_RESULT, *PIO_COMPLETION_ROUTINE_RESULT;
更多信息
方案 1: 转发不用再管
如果驱动程序只是想要转发 IRP 向下,不执行任何其他操作,请使用下面的代码。 该驱动程序没有在这种情况下设置完成例程。 如果该驱动程序是一个最高级别的驱动程序,IRP 可以完成同步或异步方式,根据较低的驱动程序返回的状态。
NTSTATUS DispatchRoutine_1( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { // // You are not setting a completion routine, so just skip the stack // location because it provides better performance. // IoSkipCurrentIrpStackLocation (Irp); return IoCallDriver(TopOfDeviceStack, Irp); }
方案 2: 前进和等待
如果要转发 IRP 到较低的驱动程序,并等待其返回,以便它可以处理 IRP 驱动程序,请使用下面的代码。 这样做通常是处理即插即用的 Irp 时。 例如,在收到 IRP_MN_START_DEVICE IRP 时,必须转发 IRP 到总线驱动程序,并等待其完成,然后才能启动您的设备。 Windows XP 系统都有一个名为IoForwardIrpSynchronously ,您可以使用轻松地执行此操作的新函数。
NTSTATUS DispatchRoutine_2( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { KEVENT event; NTSTATUS status; KeInitializeEvent(&event, NotificationEvent, FALSE); // // You are setting completion routine, so you must copy // current stack location to the next. You cannot skip a location // here. // IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, CompletionRoutine_2, &event, TRUE, TRUE, TRUE ); status = IoCallDriver(TopOfDeviceStack, Irp); if (status == STATUS_PENDING) { KeWaitForSingleObject(&event, Executive, // WaitReason KernelMode, // must be Kernelmode to prevent the stack getting paged out FALSE, NULL // indefinite wait ); status = Irp->IoStatus.Status; } // <---- Do your own work here. // // Because you stopped the completion of the IRP in the CompletionRoutine // by returning STATUS_MORE_PROCESSING_REQUIRED, you must call // IoCompleteRequest here. // IoCompleteRequest (Irp, IO_NO_INCREMENT); return status; } NTSTATUS CompletionRoutine_2( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { if (Irp->PendingReturned == TRUE) { // // You will set the event only if the lower driver has returned // STATUS_PENDING earlier. This optimization removes the need to // call KeSetEvent unnecessarily and improves performance because the // system does not have to acquire an internal lock. // KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE); } // This is the only status you can return. return STATUS_MORE_PROCESSING_REQUIRED; }
方案 3: 转发与完成例程
在这种情况下,驱动程序设置完成例程、 转发 IRP 向下,然后返回是较低的驱动程序的状态。 设置完成例程的目的是在其返回途中 IRP 的内容进行修改。
NTSTATUS DispathRoutine_3( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; // // Because you are setting completion routine, you must copy the // current stack location to the next. You cannot skip a location // here. // IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, CompletionRoutine_31,// or CompletionRoutine_32 NULL, TRUE, TRUE, TRUE ); return IoCallDriver(TopOfDeviceStack, Irp); }
- 您不得更改完成例程中 IRP 的状态。这是为了确保在 IRP 的 IoStatus 块 (Irp-> IoStatus.Status) 中设置的状态值都是相同的低级驱动程序的返回状态。
- 必须将等待状态的 IRP 的传播由 Irp-> PendingReturned。
- 您不得更改完成 IRP 的这项工作。
NTSTATUS CompletionRoutine_31 ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { // // Because the dispatch routine is returning the status of lower driver // as is, you must do the following: // if (Irp->PendingReturned) { IoMarkIrpPending( Irp ); } return STATUS_CONTINUE_COMPLETION ; // Make sure of same synchronicity } NTSTATUS CompletionRoutine_32 ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { // // Because the dispatch routine is returning the status of lower driver // as is, you must do the following: // if (Irp->PendingReturned) { IoMarkIrpPending( Irp ); } // // To make sure of the same synchronicity, complete the IRP here. // You cannot complete the IRP later in another thread because the // the dispatch routine is returning the status returned by the lower // driver as is. // IoCompleteRequest( Irp, IO_NO_INCREMENT); // // Although this is an unusual completion routine that you rarely see, // it is discussed here to address all possible ways to handle IRPs. // return STATUS_MORE_PROCESSING_REQUIRED; }
方案 4: 为更高版本,进行排队或转发和重新使用
在驱动程序要排队 IRP 以及以后对其进行处理,或正向 IRP 到较低的驱动程序和一定数量的时间之前完成 IRP 时重用它的情况下,使用下面的代码段。 调度例程标记挂起 IRP,并返回 STATUS_PENDING,因为打算以后在另一个线程完成 IRP。 在这里,完成例程可以更改 IRP,如有必要 (与以前的方案) 的状态。
NTSTATUS DispathRoutine_4( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { NTSTATUS status; // // You mark the IRP pending if you are intending to queue the IRP // and process it later. If you are intending to forward the IRP // directly, use one of the methods discussed earlier in this article. // IoMarkIrpPending( Irp ); // // For demonstration purposes: this IRP is forwarded to the lower driver. // IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, CompletionRoutine_41, // or CompletionRoutine_42 NULL, TRUE, TRUE, TRUE ); IoCallDriver(TopOfDeviceStack, Irp); // // Because you marked the IRP pending, you must return pending, // regardless of the status of returned by IoCallDriver. // return STATUS_PENDING ; }
NTSTATUS CompletionRoutine_41( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { // // By returning STATUS_CONTINUE_COMPLETION , you are relinquishing the // ownership of the IRP. You cannot touch the IRP after this. // return STATUS_CONTINUE_COMPLETION ; } NTSTATUS CompletionRoutine_42 ( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID Context ) { // // Because you are stopping the completion of the IRP by returning the // following status, you must complete the IRP later. // return STATUS_MORE_PROCESSING_REQUIRED ; }
方案 5: 完成 IRP 中调度例程
此方案演示如何完成 IRP 调度例程中。重要 调度例程的返回状态完成 IRP 中调度例程时,应与 IRP 的 IoStatus 块中设置的值的状态相匹配 (Irp-> IoStatus.Status)。
NTSTATUS DispatchRoutine_5( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { // // <-- Process the IRP here. // Irp->IoStatus.Status = STATUS_XXX; Irp->IoStatus.Information = YYY; IoCompletRequest(Irp, IO_NO_INCREMENT); return STATUS_XXX; }