上一篇说的是挂起IRP, 并在挂起IRP的时候将挂起的IRP结束, 还有另外一个办法就是取消IRP, 逐个结束. 这就是传说中的取消IRP请求. 这个取消IRP还是比较有用的, 我们很多时候在打开一个文件的时候, 如果需要等待的时间太长, 发现这个文件本来有不是太有用, 一般就会去点取消按钮. 取消当然需要内核的支持.在内核中如果要支持取消IRP, 那么首先要调用IoSetCancelRoutine, 设置一个取消例程, 这个IoSetCancelRoutine函数也是有两个作用, 如果传递的非NULL值, 那么就是设置取消例程, 如果是NULL, 那么就是删除原来设置的(取消例程).
如果是在内核中需要取消回调例程可以调用IoCancelIrp, 当然Win32调用进入内核也是调用这号例程了, 这号例程内部会调用IoAcquireCancelSpinLock获取自旋锁. 所以取消这个自旋锁的任务留给了取消例程了. 这个非常容易忘记,因为我们自己申请的内存都容易忘记释放了. 更不要说这个是人家申请的, 不过还好. 如果不释放是会崩溃的, 所以要记住这一点.还有了在取消例程中当然就不要干太多的事情了. 因为自旋锁占着茅坑呢..
当然Win32下面的CancelIo比内核里面的就强悍一点了, 其可以一次取消很多以前的IRP. 所有未决的请求都可以取消..这篇其实和上篇是差不多的, 上一篇是挂起IRP, 这里是取消IRP. 还要学习几种处理IRP的手段, 基本上写驱动就是倒腾这个了.. 所以要多学习几种手段了..
关于代码的的逻辑含义我就不说了, 和上篇差不多, 并且也没有加太多东西. 直接上代码吧, 这是用户层的.
#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( &ucBuf, sizeof(ucBuf), ';a'; ); bRet = WriteFile( hDevice, ucBuf, sizeof( ucBuf ), &dwByteWrite, &StOverLapped1 ); if ( !bRet && GetLastError() != ERROR_IO_PENDING ) { printf( "读取设备失败!\n" ); break; } RtlFillMemory( &ucBuf, sizeof(ucBuf), 'b'); bRet = WriteFile( hDevice, &ucBuf, sizeof( ucBuf ), &dwByteWrite, &StOverLapped2 ); if ( !bRet && 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; }
内核代码
#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; }