上一篇谈到了这个完成例程, 我们在完成例程中返回的是Status_Success, 这样Irp会继续向上回卷, 此时的完成例程仅仅是一个通知, 表明Irp已经完成. 但是如果此时我们不返回Status_success而是返回Status_Processing_Required则会停止回卷, 这时候的本层的Irp处理例程又重新获得Irp的控制. 并且该Irp又变成的未完成状态. 需要再次的调用IoCompleteRequest完成Irp.
这个在完成例程中返回Status_Processing_Required的情况比较郁闷就是, 完成例程执行完了以后从哪里开始执行我们的分发函数的问题. 因为底层设备是异步调用, 这时候调用完了IoCallDriver以后程序会立马返回, 这时候我们一般需要设置一个事件来等待完成例程中设置该事件, 这个很重要. 切记..
这边是代码, 关于测试驱动和用户态这边的代码都和上一个是一个套路, 就只贴关键代码了:
1
2
3
4
5
6
7
8
9
|
NTSTATUS CompletionRead(PDEVICE_OBJECT DeviceObject,PIRP Irp,PVOID Context ) {
if ( Irp->PendingReturned == TRUE ) {
//设置事件
KeSetEvent( ( PKEVENT )Context, IO_NO_INCREMENT, FALSE );
}
return STATUS_MORE_PROCESSING_REQUIRED;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
********************************************************************
读取请求处理例程
pDeviceObj :设备对象指针
pIrp :Irp指针
成功返回STATUS_SUCCESS 否则返回错误
*********************************************************************/
NTSTATUS DispatchRead( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
KEVENT Event;
NTSTATUS Status;
PDEVICE_EXT pDeviceExt = NULL;
pDeviceExt = pDeviceObj->DeviceExtension;
//将本层的IRP堆栈拷贝到底层堆栈
IoCopyCurrentIrpStackLocationToNext( pIrp );
//初始化事件
KeInitializeEvent( &Event, NotificationEvent, FALSE );
//设置完成例程
IoSetCompletionRoutine( pIrp, &CompletionRead, &Event, TRUE, TRUE, TRUE );
//调用底层驱动
Status = IoCallDriver( pDeviceExt->pLowDeviceObj, pIrp );
if ( Status == STATUS_PENDING ) {
KdPrint( ( "底层设备开始返回Pending....\nWait....\n" ) );
KeWaitForSingleObject( &Event, Executive, KernelMode , FALSE, NULL );
//完成例程处理完了以后会从这里开始执行
Status = pIrp->IoStatus.Status;
}
//虽然在底层驱动已经将IRP完成了,但是由于完成例程返回的是
//STATUS_MORE_PROCESSING_REQUIRED,因此需要再次调用IoCompleteRequest!
IoCompleteRequest ( pIrp, IO_NO_INCREMENT );
KdPrint( ( "CompletionRead:离开读取例程\n" ) );
return Status;
}
|