上一篇说的是挂起IRP, 并在挂起IRP的时候将挂起的IRP结束, 还有另外一个办法就是取消IRP, 逐个结束. 这就是传说中的取消IRP请求. 这个取消IRP还是比较有用的, 我们很多时候在打开一个文件的时候, 如果需要等待的时间太长, 发现这个文件本来有不是太有用, 一般就会去点取消按钮. 取消当然需要内核的支持.在内核中如果要支持取消IRP, 那么首先要调用IoSetCancelRoutine, 设置一个取消例程, 这个IoSetCancelRoutine函数也是有两个作用, 如果传递的非NULL值, 那么就是设置取消例程, 如果是NULL, 那么就是删除原来设置的(取消例程).

如果是在内核中需要取消回调例程可以调用IoCancelIrp, 当然Win32调用进入内核也是调用这号例程了, 这号例程内部会调用IoAcquireCancelSpinLock获取自旋锁. 所以取消这个自旋锁的任务留给了取消例程了. 这个非常容易忘记,因为我们自己申请的内存都容易忘记释放了. 更不要说这个是人家申请的, 不过还好. 如果不释放是会崩溃的, 所以要记住这一点.还有了在取消例程中当然就不要干太多的事情了. 因为自旋锁占着茅坑呢..

当然Win32下面的CancelIo比内核里面的就强悍一点了, 其可以一次取消很多以前的IRP. 所有未决的请求都可以取消..这篇其实和上篇是差不多的, 上一篇是挂起IRP, 这里是取消IRP. 还要学习几种处理IRP的手段, 基本上写驱动就是倒腾这个了.. 所以要多学习几种手段了..

关于代码的的逻辑含义我就不说了, 和上篇差不多, 并且也没有加太多东西. 直接上代码吧, 这是用户层的.

 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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*	Windows 内核下取消IRP 3环代码
	编译方法参见makefile. TAB = 8
*/
#include <windows.h>;
#include <stdio.h>;

#pragma comment( linker, "/Entry:Jmain" )
#pragma comment( linker, "/subsystem:console" )

#define SYS_LINK_NAME	"\\\\.\\SysCancelIrp"
//===========================================================================
//入口
//===========================================================================
int Jmain( ) {
	BOOL bRet;
	ULONG dwByteWrite;
	UCHAR ucBuf[10];
	HANDLE hDevice = INVALID_HANDLE_VALUE;
	OVERLAPPED StOverLapped1 = {0};
	OVERLAPPED StOverLapped2 = {0};

	do {
		//打开设备
		hDevice = CreateFile( SYS_LINK_NAME, GENERIC_READ | GENERIC_WRITE,
				      0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );
		if ( hDevice == INVALID_HANDLE_VALUE ) {
			printf( "打开设备失败!\n" );
			break;
		}

		RtlFillMemory( &amp;ucBuf, sizeof(ucBuf), &#39;a&#39; );
		bRet = WriteFile( hDevice, ucBuf, sizeof( ucBuf ), &amp;dwByteWrite, &amp;StOverLapped1 );
		if ( !bRet &amp;&amp; GetLastError() != ERROR_IO_PENDING ) {
			printf( "读取设备失败!\n" );
			break;
		}

		RtlFillMemory( &amp;ucBuf, sizeof(ucBuf), &#39;b&#39; );
		bRet = WriteFile( hDevice, &amp;ucBuf, sizeof( ucBuf ), &amp;dwByteWrite, &amp;StOverLapped2 );
		if ( !bRet &amp;&amp; GetLastError() != ERROR_IO_PENDING ) {
			printf( "读取设备失败!\n" );
			break;
		}

		printf( "打开设备, 并且发送两次IRP请求成功!\n" );

		Sleep( 2000 );				// 迫使程序暂停2秒
		CancelIo( hDevice );		//创建IRP_MJ_CLEANUP Irp
	} while ( FALSE );

	if ( hDevice != INVALID_HANDLE_VALUE ) {
		CloseHandle( hDevice );
	}
	system( "pause" );
	return 0;
}

这边是内核的代码:

  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
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*	Windows 内核下取消IRP 0环代码
	编译方法参见makefile. TAB = 8
*/
#include <ntddk.h>

//---------------------------------------------------------------------------
#define DEVICE_NAME	L"\\Device\\CancelIrp"
#define SYS_LINK_NAME	L"\\??\\SysCancelIrp"

typedef struct tagDeviceExt {
	PDEVICE_OBJECT pDeviceObj;
	UNICODE_STRING USzDeviceName;
	UNICODE_STRING USzSysLinkName;
} DEVICE_EXT, *PDEVICE_EXT;

//===========================================================================
//驱动卸载例程
//===========================================================================
#pragma code_seg( "PAGE" )
VOID DriverUnload( PDRIVER_OBJECT pDriverObj ) {
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pNextDeviceObj = NULL;

	pNextDeviceObj = pDriverObj->DeviceObject;

	while( pNextDeviceObj != NULL ) {

		pDeviceExt = pNextDeviceObj->DeviceExtension;

		IoDeleteDevice( pDeviceExt->pDeviceObj );

		IoDeleteSymbolicLink( &pDeviceExt->USzSysLinkName );

		KdPrint( ( "删除%wZ设备成功!\n", &pDeviceExt->USzDeviceName ) );

		pNextDeviceObj = pNextDeviceObj->NextDevice;
	}
}
//===========================================================================
//所有不关心的IRP处理
//===========================================================================
NTSTATUS DispatchRoutine( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {

	PAGED_CODE();
	pIrp->IoStatus.Information = 0;
	pIrp->IoStatus.Status = STATUS_SUCCESS;
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	return STATUS_SUCCESS;
}
//===========================================================================
//取消例程(取消例程的时候其实和堆栈差不多. 先进先出)
//===========================================================================
#pragma code_seg()
VOID CancelIrp( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	ULONG i;
	ULONG ulWriteLength;
	ULONG ulWriteOffset;
	PDEVICE_EXT pDeviceExt = NULL;
	PIO_STACK_LOCATION Stack = NULL;

	PAGED_CODE_LOCKED();

	pDeviceExt = pDeviceObj->DeviceExtension;
	Stack = IoGetCurrentIrpStackLocation(pIrp);

	//欲读取长度
	ulWriteLength = Stack->Parameters.Write.Length;
	ulWriteOffset = ( ULONG )Stack->Parameters.Write.ByteOffset.QuadPart;           

	for( i = 0; i < ulWriteLength; i++ ) {
		KdPrint( ( "%c\t", *( ( ( UCHAR* )pIrp->AssociatedIrp.SystemBuffer ) + i ) ) );
	}
	KdPrint(( "\n" ));

	//设置完成状态为STATUS_CANCELLED
	pIrp->IoStatus.Status = STATUS_CANCELLED;
	pIrp->IoStatus.Information = 0;

	//结束IRP请求
	IoCompleteRequest( pIrp, IO_NO_INCREMENT );

	//此处释放Cancel自旋锁(注意, 这里不是自己获取的, 但是要释放, 很容易忘却)
	IoReleaseCancelSpinLock( pIrp->CancelIrql );

	KdPrint( ( "离开取消例程!\n" ) );
}
//===========================================================================
//读请求处理
//===========================================================================
#pragma code_seg( "PAGE" )
NTSTATUS DispatchWrite( PDEVICE_OBJECT pDeviceObj, PIRP pIrp ) {
	PDEVICE_EXT pDeviceExt = NULL;

	pDeviceExt = ( PDEVICE_EXT )pDeviceObj->DeviceExtension;

	//设置卸载例程(也可以取消卸载例程)
	IoSetCancelRoutine( pIrp, CancelIrp );

	//将IRP设置为STATUS_CANCELLED
	pIrp->IoStatus.Information = 0;
	pIrp->IoStatus.Status = STATUS_CANCELLED;

	//将IRP设置为挂起状态
	IoMarkIrpPending( pIrp );
	KdPrint( ( "DispatchRead挂起!\n" ) );

	return STATUS_PENDING;
}
//===========================================================================
//驱动入口
//===========================================================================
#pragma code_seg( "INIT" )
NTSTATUS DriverEntry( PDRIVER_OBJECT pDriverObj, PUNICODE_STRING pUSzRegPath ) {
	ULONG i;
	NTSTATUS Status;
	PDEVICE_EXT pDeviceExt = NULL;
	PDEVICE_OBJECT pDeviceObj = NULL;
	UNICODE_STRING USzDeviceName = RTL_CONSTANT_STRING( DEVICE_NAME );
	UNICODE_STRING USzSysLinkName = RTL_CONSTANT_STRING( SYS_LINK_NAME );

	//创建设备
	Status = IoCreateDevice( pDriverObj, sizeof( DEVICE_EXT ), &USzDeviceName,
				 FILE_DEVICE_UNKNOWN, 0, TRUE, &pDeviceObj );
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "设备创建失败!\n" ) );
		return Status;
	}

	//创建符号链接
	Status = IoCreateSymbolicLink( &USzSysLinkName, &USzDeviceName );
	if ( !NT_SUCCESS( Status ) ) {
		KdPrint( ( "创建符号链接失败!\n" ) );
		return Status;
	}

	//设置设备扩展和设备属性
	pDeviceObj->Flags |= DO_BUFFERED_IO;
	pDeviceExt = ( PDEVICE_EXT )pDeviceObj->DeviceExtension;
	pDeviceExt->pDeviceObj = pDeviceObj;
	pDeviceExt->USzDeviceName = USzDeviceName;
	pDeviceExt->USzSysLinkName = USzSysLinkName;

	//设置分发函数
	for( i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++ ) {
		pDriverObj->MajorFunction[i] = &DispatchRoutine;
	}
	pDriverObj->MajorFunction[IRP_MJ_WRITE] = &DispatchWrite;
	pDriverObj->DriverUnload = DriverUnload;

	return Status;
}