首先看看IoCopyCurrentIrpStackLocationToNext的实现:
#define IoCopyCurrentIrpStackLocationToNext( Irp ) { \ PIO_STACK_LOCATION __irpSp; \ PIO_STACK_LOCATION __nextIrpSp; \ __irpSp = IoGetCurrentIrpStackLocation( (Irp) ); \ __nextIrpSp = IoGetNextIrpStackLocation( (Irp) ); \ RtlCopyMemory(__nextIrpSp, __irpSp, FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); \ __nextIrpSp->Control = 0; }
实际上就是把IO_STACK_LOCATION栈的本层对象拷贝到下一层,但是不拷贝CompletionRoutine和Context这两个域,也就是说只拷贝CompletionRoutine之前的域。(CompletionRoutine完成例程,Context完成例程的参数)
再看看IoSetCompletionRoutine的实现:
#define IoSetCompletionRoutine(irp,routine,completioncontext,success,error,cancel)\ #{ PIO_STACK_LOCATION irpsp;\ #ASSERT((success)|(error)|(cancel)?(routine)!=NULL:TRUE);\ #irpsp=IoGetNextIrpStackLocation((irp));\ #irpsp->completionroutine=(routine);\ #irpsp->context=(completioncontext);\ #irpsp->control=0;\ #if((success)){irpsp->control=SL_INVOKE_ON_SUCCESS;}\ #if((error)){irpsp->control |= SL_INVOKE_ON_ERROR;}\ #if((cancel)){irpsp->control |= SL_INVOKE_ON_CANCEL;} }\
刚开始看书的时候老师在这个位置产生疑惑,在本层调用IoSetCompletionRoutine设置下层驱动的完成例程时,为什么传进去的参数irp里指向的CurrentStackLocation还是本层的,而没有让它指向下一层的,这样子设置的完成例程不是为本层设置了吗。
现在看了实现才知道,原来IoSetCompletionRoutine实现里有取得下一层的IO_STACK_LOCATION对象,设置的完成例程也是为下一层的驱动设置的。
再看看IoCallDriver的部分实现:
函数声明如下所示:
-
NTSTATUS IoCallDriver(
-
_In_ PDEVICE_OBJECT DeviceObject,
-
_Inout_ PIRP Irp
-
);
-
部分实现如下所示:
- Irp->CurrentLocation--;
- if (Irp->CurrentLocation <= 0) {
- KiBugCheck3( NO_MORE_IRP_STACK_LOCATIONS, (ULONG_PTR) Irp, 0, 0 );
- }
- irpSp = IoGetNextIrpStackLocation( Irp );
- Irp->Tail.Overlay.CurrentStackLocation = irpSp;
- //
- // Save a pointer to the device object for this request so that it can
- // be used later in completion.
- //
- irpSp->DeviceObject = DeviceObject;
- //
- // Invoke the driver at its dispatch routine entry point.
- //
- driverObject = DeviceObject->DriverObject;
- //
- // Prevent the driver from unloading.
- //
- status = driverObject->MajorFunction[irpSp->MajorFunction]( DeviceObject,
- Irp );
这里一定要注意的是参数_In_ PDEVICE_OBJECT DeviceObject必须是下层(也就是目标层)的设备对象,不能再把本层的设备对象传进去了,一般下层的设备对象都存储在设备扩展对象里。
由实现代码可知,根据传进去的下层的设备对象可以得到下层的驱动对象,然后就可以根据IRP的类型来调用下层驱动的各种派遣函数了。