首先看看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的类型来调用下层驱动的各种派遣函数了。
本文深入探讨了Windows驱动程序中IRP栈的操作机制,详细解释了IoCopyCurrentIrpStackLocationToNext和IoSetCompletionRoutine函数如何处理IRP栈位置,以及IoCallDriver如何调用下层驱动的派遣函数。
&spm=1001.2101.3001.5002&articleId=82690095&d=1&t=3&u=a28af85b275a4985b7982b02739b6ece)
156

被折叠的 条评论
为什么被折叠?



