上一篇谈到了这个完成例程, 我们在完成例程中返回的是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;
}