Windows驱动开发WDM (16)- 完成例程 (重新获得IRP控制权)

来源:互联网 发布:python replace 正则 编辑:程序博客网 时间:2024/05/17 23:21

前一次(http://blog.csdn.net/zj510/article/details/8350184)学习了完成例程返回STATUS_SUCCESS的情况,也就是驱动调用IoCallDriver后不会再获得IRP。这次来看看再次获得IRP控制权的情况。

 

设置完成例程

先看看设置完成例程那里的代码:

IoCopyCurrentIrpStackLocationToNext(Irp);KEVENT event;KeInitializeEvent(&event, NotificationEvent, FALSE);//创建一个event对象。//通过第三个参数将event对象传递给完成例程IoSetCompletionRoutine(Irp, MyIoCompletion, &event, TRUE, TRUE, TRUE);status = IoCallDriver(pdx->NextStackDevice, Irp);if (status == STATUS_PENDING){//如果下层驱动是异步完成irp的话,就等待event被触发(完成例程会触发)//如果下层驱动是同步完成irp或者其他错误,那么就无需等待了。直接向上返回。KeWaitForSingleObject(&event,Executive,KernelMode ,FALSE,NULL);//等待完成例程触发这个事件,最后一个参数是NULL,表示无限等待status = Irp->IoStatus.Status;//获取下层驱动的irp状态}//获取内核模式下的地址,这个地址一定> 0x7FFFFFFFchar* outBuf = (char*)MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);outBuf[1] = outBuf[2];//将第三个字节复制到第二个字节//虽然在底层驱动已经将IRP完成了,但是由于完成例程返回的是//STATUS_MORE_PROCESSING_REQUIRED,因此需要再次调用IoCompleteRequest!IoCompleteRequest (Irp, IO_NO_INCREMENT);

跟前一个版本相比,有3个分别:

1. 创建一个事件对象,初始化,并且在IoCallDriver后面等待这个事件;

2. 事件触发后,将输出缓冲的第三个字节赋值给第二个字节,也就是说过滤驱动再更改一下返回的内容;

3. 调用IoCompleteRequest再次完成IRP(因为完成例程返回了STATUS_MORE_PROCESSING_REQUIRED)

IoSetCompletionRoutine的第三个参数对应完成例程的第三个参数。也就是说我们可以传一些数据给完成例程。

VOID IoSetCompletionRoutine(  _In_      PIRP Irp,  _In_opt_  PIO_COMPLETION_ROUTINE CompletionRoutine,  _In_opt_  PVOID Context,  _In_      BOOLEAN InvokeOnSuccess,  _In_      BOOLEAN InvokeOnError,  _In_      BOOLEAN InvokeOnCancel);

 

完成例程代码

NTSTATUS MyIoCompletion(IN PDEVICE_OBJECT fdo, IN PIRP irp, IN PVOID context){KdPrint(("Enter MyIoCompletion\n"));//判断是否挂起返回,就是说下层驱动是挂起irp,然后再处理irp(异步irp)if(irp->PendingReturned){//设置完成例程那里,只有下层驱动返回pending状态才等待事件,那么我们//这里只需要挂起返回的时候才设置事件信号KeSetEvent((PKEVENT)context, IO_NO_INCREMENT, FALSE);}KdPrint(("Leave MyIoCompletion\n"));//返回STATUS_MORE_PROCESSING_REQUIRED,当前驱动将再次获得irp的控制权,//同时也需要再次调用IoCompleteRequest来完成irpreturn STATUS_MORE_PROCESSING_REQUIRED;}

跟前面的版本比较:

1. 当挂起返回的时候,给事件设置信号,这个事件对象是参数传进来的。

2. 返回STATUS_MORE_PROCESSING_REQUIRED

因为完成例程里面设置了信号,那么KeWaitForSingleObject就会返回。

 

测试代码:

跟前一个文章一样,相当的简单,就是起2个线程,发2个IRP请求:

// TestWDMDriver.cpp : Defines the entry point for the console application.//#include "stdafx.h"#include <windows.h>#include <process.h>#define DEVICE_NAME L"\\\\.\\HelloWDM"#define DEVICE_NAME2 L"\\\\.\\HelloWDM2"#define DEVICE_EX L"\\\\.\\HelloWDM_EX"void Test(void* pParam){int index = (int)pParam;//设置overlapped标志,表示异步打开HANDLE hDevice;hDevice = CreateFile(DEVICE_NAME,GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_OVERLAPPED,NULL);wprintf(L"CreateFile, name: %s, ret: %x\n", DEVICE_NAME, hDevice);if (hDevice != INVALID_HANDLE_VALUE){char inbuf[100] = {0};sprintf(inbuf, "hello world %d", index);char outbuf[100] = {0};DWORD dwBytes = 0;DWORD dwStart = GetTickCount();printf("input buffer: %s\n", inbuf);OVERLAPPED ol = {0};ol.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);BOOL b = DeviceIoControl(hDevice, CTL_CODE(FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT, FILE_ANY_ACCESS), inbuf, strlen(inbuf), outbuf, 100, &dwBytes, &ol);printf("DeviceIoControl thread %d, returns %d, last error: %d, used: %d ms, input: %s\n", index, b, GetLastError(), GetTickCount() - dwStart, inbuf);WaitForSingleObject(ol.hEvent, INFINITE);DWORD dwEnd = GetTickCount();//将输出buffer的数据和'm'亦或,看看是否能够得到初始的字符串。for (int i = 0; i < strlen(inbuf); i++){outbuf[i] = outbuf[i] ^ 'm';}printf("Verify thread %d, outbuf: %s, used: %d ms\n", index, outbuf, dwEnd - dwStart);CloseHandle(hDevice);}elseprintf("CreateFile failed, err: %x\n", GetLastError());}int _tmain(int argc, _TCHAR* argv[]){HANDLE t1 = (HANDLE)_beginthread(Test, 0, (void*)0);_beginthread(Test, 0, (void*)1);WaitForSingleObject(t1, INFINITE);printf("Test ends\n");return 0;}


看看输出结果:

可以看到原来输出是xello world 0和xello world 1,现在第二个字节变成l了。这是因为过滤驱动在下层驱动完成后,又再次更改了输出内容,就是将第三个字节赋值给第二个字节了。

 

代码:http://download.csdn.net/detail/zj510/4913259

目录1: 功能驱动,从控制面板里面安装

目录2: 过滤驱动,直接右键inf文件安装,同时再改一个注册表,参考http://blog.csdn.net/zj510/article/details/8332658

测试代码:一段简单的测试代码,起2个线程,发2个IRP请求。

WDK7600编译驱动,VS2008编译测试代码。

原创粉丝点击