将前面的例子扩展一下,这次让这个驱动模拟一个文件,支持读写和获取文件长度。
在驱动中创建一块存储,用来模拟保存文件数据
首先给设备扩展增加2个内容:buffer和filelen。buffer模拟一个存储,filelen指文件长度。
typedef struct _DEVICE_EXTENSION { PDEVICE_OBJECT fdo; PDEVICE_OBJECT NextStackDevice; UNICODE_STRING ustrDeviceName; // 设备名 UNICODE_STRING ustrSymLinkName; // 符号链接名 char* buffer; ULONG filelen; } DEVICE_EXTENSION, *PDEVICE_EXTENSION;
创建FDO(AddDevice函数)成功后,分配一些内核模式下的内存
#define MAX_FILE_LEN 100//最多保存100byte pdx->buffer = (char*)ExAllocatePool(NonPagedPool, MAX_FILE_LEN); pdx->filelen = 0;
ExAllocatePool是内核模式下分配内存的函数。这里分配100字节,也就是说最大保存100字节的内容,然后将这段空间的起始地址记录在设备扩展里面。
派遣函数转发WRITE,READ和FILESIZE的请求到相应处理函数
增加IRP_MJ_WRITE,IRP_MJ_READ和IRP_MJ_QUERY_INFORMATION的处理函数,并在派遣函数里面加入相应的派遣代码,如:
#pragma PAGEDCODE NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { PAGED_CODE(); KdPrint(("Enter HelloWDMDispatchRoutine\n")); PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); //打印IRP的major function code和minor function code。 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; KdPrint(("DispatchRoutine, Major function code: %x, Minor function code: %x, control code: %x\n", stack->MajorFunction, stack->MinorFunction, code)); switch (stack->MajorFunction) { case IRP_MJ_WRITE: HelloWDMWrite(fdo, Irp); break; case IRP_MJ_READ: HelloWDMRead(fdo, Irp); break; case IRP_MJ_QUERY_INFORMATION: HelloWDMQueryInformation(fdo, Irp); break; default: { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; // no bytes xfered IoCompleteRequest( Irp, IO_NO_INCREMENT ); } break; } KdPrint(("Leave HelloWDMDispatchRoutine\n")); return Irp->IoStatus.Status; }
只处理3种IRP,其他的IRP不做任何处理,直接完成IRP。
IRP_MJ_WRITE处理函数
NTSTATUS HelloWDMWrite(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { KdPrint(("Enter HelloWDMWrite\n")); NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;//获得设备扩展 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//获得irp信息 ULONG ulWrite = 0;//成功Write的字节数 ULONG ulLen = stack->Parameters.Write.Length;//获取caller想要write的字节数 ULONG ulOffset = (ULONG)stack->Parameters.Write.ByteOffset.QuadPart;//获取caller调用(WriteFile)的偏移量 if (ulOffset + ulLen > MAX_FILE_LEN) {//超过驱动的缓冲边界,无法write了,就返回错误 status = STATUS_FILE_INVALID; ulWrite = 0; } else { //把内容copy到驱动的缓冲里面。这里使用的是DO_BUFFERED_IO模式,那么可以从Irp->AssociatedIrp.SystemBuffer获取 //内核模式下的地址(系统会将用户模式的缓冲COPY到内核模式的缓冲Irp->AssociatedIrp.SystemBuffer) RtlCopyMemory(pdx->buffer + ulOffset, Irp->AssociatedIrp.SystemBuffer, ulLen);//保存在驱动里面 status = STATUS_SUCCESS; ulWrite = ulLen; //保存多次调用WriteFile后的最大值,这个例子里面,文件长度只会增长,不会减小。 if (ulOffset + ulLen > pdx->filelen) { pdx->filelen = ulOffset + ulLen; } } Irp->IoStatus.Status = status; //设置IRP的完成状态(成功还是失败) Irp->IoStatus.Information = ulWrite; //驱动实际操作了多少字节 IoCompleteRequest(Irp, IO_NO_INCREMENT); //结束IRP,无需往下面的驱动传递 KdPrint(("Leave HelloWDMWrite, write: %d bytes, offset: %d, input len: %d\n", ulWrite, ulOffset, ulWrite)); return status; }
这个处理函数主要就是将Irp->AssociatedIrp.SystemBuffer(系统会将用户模式地址的缓冲区内存复制到SystemBuffer里面)的内容复制到驱动的存储里面进行保存。
IRP_MJ_READ处理函数
NTSTATUS HelloWDMRead(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { KdPrint(("Enter HelloWDMRead\n")); PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); ULONG ulRead = 0;//实际读取的字节数 ULONG ulLen = stack->Parameters.Read.Length;//caller想要读取的字节数 ULONG ulOffset = stack->Parameters.Read.ByteOffset.QuadPart;//caller想要读取的偏移量 NTSTATUS status = STATUS_SUCCESS; if (ulOffset + ulLen > MAX_FILE_LEN) {//超出驱动缓冲边界,读取失败 status = STATUS_FILE_INVALID; ulRead = 0; } else { //将驱动缓冲(模拟的文件)中的内存COPY到AssociatedIrp.SystemBuffer中。 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, pdx->buffer + ulOffset, ulLen); ulRead = ulLen; status = STATUS_SUCCESS; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = ulRead; IoCompleteRequest(Irp, IO_NO_INCREMENT); //结束IRP,无需往下面的驱动传递 KdPrint(("Leave HelloWDMRead, read: %d bytes, offset: %d, input len: %d\n", ulRead, ulOffset, ulLen)); return status; }
将驱动缓冲保存的数据复制到IRP->AssociatedIrp.SystemBuffer中,这样当IRP_MJ_READ的IRP结束后,系统会将SystemBuffer指向的内容复制到用户模式的buffer中,从而使得ReadFile的buffer可以接收到驱动填充的数据。
IRP_MJ_QUERY_INFORMATION处理函数
NTSTATUS HelloWDMQueryInformation(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { KdPrint(("Enter HelloWDMQueryInformation\n")); PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); FILE_INFORMATION_CLASS info = stack->Parameters.QueryFile.FileInformationClass; NTSTATUS status = STATUS_SUCCESS; //IRP_MJ_QUERY_INFORMATION可以获得不同的信息,GetFileSize触发的IRP_MJ_QUERY_INFORMATION对应的是 //FileStandardInformation。 if (info == FileStandardInformation) {//处理GetFileSize请求 KdPrint(("FileStandardInformation\n")); //获取缓冲区数据 PFILE_STANDARD_INFORMATION file_info = (PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer; file_info->EndOfFile = RtlConvertLongToLargeInteger(pdx->filelen);//修改FILE_STANDARD_INFORMATION的EndOfFile属性, } else {//此驱动不处理任何其他的IRP_MJ_QUERY_INFORMATION请求 status = STATUS_FILE_INVALID; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = stack->Parameters.QueryFile.Length; IoCompleteRequest(Irp, IO_NO_INCREMENT); KdPrint(("Leave HelloWDMQueryInformation, operated: %d bytes\n", Irp->IoStatus.Information)); return status; }
IRP_MJ_QUERY_INFORMATION请求可以处理文件长度,文件创建时间等等信息,这里我们只处理文件长度,stack->Parameters.QueryFile.FileInformationClass保存了一个类型,对于文件长度,它的值是FileStandardInformation。当IRP_MJ_QUERY_INFORMATION用于获取文件长度的时候,Irp->AssociatedIrp.SystemBuffer指向一个FILE_STANDARD_INFORMATION的结构。它的结构如下:
typedef struct FILE_STANDARD_INFORMATION { LARGE_INTEGER AllocationSize; LARGE_INTEGER EndOfFile; ULONG NumberOfLinks; BOOLEAN DeletePending; BOOLEAN Directory; } FILE_STANDARD_INFORMATION, *PFILE_STANDARD_INFORMATION;
说明:
其中EndOfFile是文件结束位置的偏移量,也可以认为是文件长度。设置文件长度:
file_info->EndOfFile = RtlConvertLongToLargeInteger(pdx->filelen);
通常用户模式下可以调用API GetFileSize来获取文件的长度, GetFileSize会触发IRP_MJ_QUERY_INFORMATION请求,驱动将驱动里面保存的文件长度设置给file_info->EndOfFile。这样GetFileSize就可以获得这个长度值。
存储释放
驱动程序里面分配的存储需要在驱动卸载的时候释放,AddDevice里面将分配内存的地址保存到了设备扩展中,这样在RemoveDevice函数里面可以根据设备扩展释放内存。
#pragma PAGEDCODE NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp) { PAGED_CODE(); KdPrint(("Enter HandleRemoveDevice\n")); //设置IRP的完成状态。 Irp->IoStatus.Status = STATUS_SUCCESS; //将IRP请求向底层驱动转发。 NTSTATUS status = DefaultPnpHandler(pdx, Irp); //删除符号链接 IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName); //调用IoDetachDevice()把fdo从设备栈中脱开: if (pdx->NextStackDevice) IoDetachDevice(pdx->NextStackDevice); //释放缓冲 if (pdx->buffer) { ExFreePool(pdx->buffer); } //删除fdo: IoDeleteDevice(pdx->fdo); KdPrint(("Leave HandleRemoveDevice\n")); return status; }
主要函数都介绍完了,我这里画了个简单的序列图:
完整驱动代码
贴出全部的驱动代码,主要就是增加了IRP_MJ_WRITE, IRP_MJ_READ和IRP_MJ_QUERY_INFORMATION的处理,然后驱动里面申请了一块内存,用来模拟保存文件数据。
/************************************************************************ * 文件名称:HelloWDM.cpp * 作 者:张帆 * 完成日期:2007-11-1 *************************************************************************/ #include "HelloWDM.h" #define MAX_FILE_LEN 100//最多保存100byte /************************************************************************ * 函数名称:DriverEntry * 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象 * 参数列表: pDriverObject:从I/O管理器中传进来的驱动对象 pRegistryPath:驱动程序在注册表的中的路径 * 返回 值:返回初始化驱动状态 *************************************************************************/ #pragma INITCODE extern "C" NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegistryPath) { KdPrint(("Enter DriverEntry\n")); pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice; pDriverObject->MajorFunction[IRP_MJ_PNP] = HelloWDMPnp; pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = pDriverObject->MajorFunction[IRP_MJ_CREATE] = pDriverObject->MajorFunction[IRP_MJ_READ] = pDriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] = pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloWDMDispatchRoutine; pDriverObject->DriverUnload = HelloWDMUnload; KdPrint(("Leave DriverEntry\n")); return STATUS_SUCCESS; } /************************************************************************ * 函数名称:HelloWDMAddDevice * 功能描述:添加新设备 * 参数列表: DriverObject:从I/O管理器中传进来的驱动对象 PhysicalDeviceObject:从I/O管理器中传进来的物理设备对象 * 返回 值:返回添加新设备状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS HelloWDMAddDevice(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject) //DriverObject就是指向本驱动程序的一个对象,是由PNP管理器传递进来的。 //PhysicalDeviceObject是PNP管理器传递进来的底层驱动设备对象,这个东西在NT驱动中是没有的。通常称之为PDO,确切的说是由总线驱动创建的。 { PAGED_CODE(); KdPrint(("Enter HelloWDMAddDevice\n")); NTSTATUS status; PDEVICE_OBJECT fdo; UNICODE_STRING devName; RtlInitUnicodeString(&devName,L"\\Device\\MyWDMDevice");//设备名称,设备名称只能被内核模式下的其他驱动所识别。 //创建FDO(Function Device Object) status = IoCreateDevice( DriverObject, sizeof(DEVICE_EXTENSION), &(UNICODE_STRING)devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &fdo); if( !NT_SUCCESS(status)) return status; PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension; pdx->fdo = fdo; //将FDO附加在PDO上面,并且将Extension中的NextStackDevice指向FDO的下层设备。如果PDO上面有过滤驱动的话,NextStackDevice就是过滤驱动,如果没有就是PDO。 pdx->NextStackDevice = IoAttachDeviceToDeviceStack(fdo, PhysicalDeviceObject); //创建一个缓冲,用来模拟文件。 pdx->buffer = (char*)ExAllocatePool(NonPagedPool, MAX_FILE_LEN); pdx->filelen = 0; UNICODE_STRING symLinkName; //创建链接符号,这样用户模式的应用就可以访问此设备。内核模式下,符号链接是以\??\开头的(或者\DosDevices\)。用户模式下则是\\.\开头。 //这里就可以在用户模式下用\\.\HelloWDM来访问本设备。 RtlInitUnicodeString(&symLinkName,L"\\DosDevices\\HelloWDM"); pdx->ustrDeviceName = devName; pdx->ustrSymLinkName = symLinkName; status = IoCreateSymbolicLink(&(UNICODE_STRING)symLinkName,&(UNICODE_STRING)devName); if( !NT_SUCCESS(status)) { IoDeleteSymbolicLink(&pdx->ustrSymLinkName); status = IoCreateSymbolicLink(&symLinkName,&devName); if( !NT_SUCCESS(status)) { return status; } } fdo->Flags |= DO_BUFFERED_IO | DO_POWER_PAGABLE;//DO_BUFFERED_IO,定义为“缓冲内存设备” fdo->Flags &= ~DO_DEVICE_INITIALIZING;//将Flag上的DO_DEVICE_INITIALIZING位清零,保证设备初始化完毕,必须的。 KdPrint(("Leave HelloWDMAddDevice\n")); return STATUS_SUCCESS; } /************************************************************************ * 函数名称:DefaultPnpHandler * 功能描述:对PNP IRP进行缺省处理 * 参数列表: pdx:设备对象的扩展 Irp:从IO请求包 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS DefaultPnpHandler(PDEVICE_EXTENSION pdx, PIRP Irp) { PAGED_CODE(); KdPrint(("Enter DefaultPnpHandler\n")); IoSkipCurrentIrpStackLocation(Irp); KdPrint(("Leave DefaultPnpHandler\n")); return IoCallDriver(pdx->NextStackDevice, Irp);//将irp传递给下层驱动 } /************************************************************************ * 函数名称:HandleRemoveDevice * 功能描述:对IRP_MN_REMOVE_DEVICE IRP进行处理 * 参数列表: fdo:功能设备对象 Irp:从IO请求包 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS HandleRemoveDevice(PDEVICE_EXTENSION pdx, PIRP Irp) { PAGED_CODE(); KdPrint(("Enter HandleRemoveDevice\n")); //设置IRP的完成状态。 Irp->IoStatus.Status = STATUS_SUCCESS; //将IRP请求向底层驱动转发。 NTSTATUS status = DefaultPnpHandler(pdx, Irp); //删除符号链接 IoDeleteSymbolicLink(&(UNICODE_STRING)pdx->ustrSymLinkName); //调用IoDetachDevice()把fdo从设备栈中脱开: if (pdx->NextStackDevice) IoDetachDevice(pdx->NextStackDevice); //释放缓冲 if (pdx->buffer) { ExFreePool(pdx->buffer); } //删除fdo: IoDeleteDevice(pdx->fdo); KdPrint(("Leave HandleRemoveDevice\n")); return status; } /************************************************************************ * 函数名称:HelloWDMPnp * 功能描述:对即插即用IRP进行处理 * 参数列表: fdo:功能设备对象 Irp:从IO请求包 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS HelloWDMPnp(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { PAGED_CODE(); KdPrint(("Enter HelloWDMPnp\n")); NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;//DEVICE_EXTENSION是在AddDevice里面创建的。 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//获取irp信息 switch (stack->MinorFunction)//根据PNP irp的minor function code来调用相应的处理函数。 { case IRP_MN_REMOVE_DEVICE: status = HandleRemoveDevice(pdx, Irp); break; default: status = DefaultPnpHandler(pdx, Irp); break; } KdPrint(("Leave HelloWDMPnp\n")); return status; } /************************************************************************ * 函数名称:HelloWDMDispatchRoutine * 功能描述:对缺省IRP进行处理 * 参数列表: fdo:功能设备对象 Irp:从IO请求包 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE NTSTATUS HelloWDMDispatchRoutine(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { PAGED_CODE(); KdPrint(("Enter HelloWDMDispatchRoutine\n")); PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); //打印IRP的major function code和minor function code。 ULONG code = stack->Parameters.DeviceIoControl.IoControlCode; KdPrint(("DispatchRoutine, Major function code: %x, Minor function code: %x, control code: %x\n", stack->MajorFunction, stack->MinorFunction, code)); switch (stack->MajorFunction) { case IRP_MJ_WRITE: HelloWDMWrite(fdo, Irp); break; case IRP_MJ_READ: HelloWDMRead(fdo, Irp); break; case IRP_MJ_QUERY_INFORMATION: HelloWDMQueryInformation(fdo, Irp); break; default: { Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; // no bytes xfered IoCompleteRequest( Irp, IO_NO_INCREMENT ); } break; } KdPrint(("Leave HelloWDMDispatchRoutine\n")); return Irp->IoStatus.Status; } /************************************************************************ * 函数名称:HelloWDMUnload * 功能描述:负责驱动程序的卸载操作 * 参数列表: DriverObject:驱动对象 * 返回 值:返回状态 *************************************************************************/ #pragma PAGEDCODE void HelloWDMUnload(IN PDRIVER_OBJECT DriverObject) { PAGED_CODE(); KdPrint(("Enter HelloWDMUnload\n")); KdPrint(("Leave HelloWDMUnload\n")); } /*********************************************************************** * 函数名称:HelloWDMWrite * 功能描述:处理IRP_MJ_WRITE请求 * 参数列表: fdo 设备对象FDO Irp 对应于IRP_MJ_WRITE的IRP * 返回 值:返回状态 ***********************************************************************/ NTSTATUS HelloWDMWrite(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { KdPrint(("Enter HelloWDMWrite\n")); NTSTATUS status = STATUS_SUCCESS; PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension;//获得设备扩展 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);//获得irp信息 ULONG ulWrite = 0;//成功Write的字节数 ULONG ulLen = stack->Parameters.Write.Length;//获取caller想要write的字节数 ULONG ulOffset = (ULONG)stack->Parameters.Write.ByteOffset.QuadPart;//获取caller调用(WriteFile)的偏移量 if (ulOffset + ulLen > MAX_FILE_LEN) {//超过驱动的缓冲边界,无法write了,就返回错误 status = STATUS_FILE_INVALID; ulWrite = 0; } else { //把内容copy到驱动的缓冲里面。这里使用的是DO_BUFFERED_IO模式,那么可以从Irp->AssociatedIrp.SystemBuffer获取 //内核模式下的地址(系统会将用户模式的缓冲COPY到内核模式的缓冲Irp->AssociatedIrp.SystemBuffer) RtlCopyMemory(pdx->buffer + ulOffset, Irp->AssociatedIrp.SystemBuffer, ulLen);//保存在驱动里面 status = STATUS_SUCCESS; ulWrite = ulLen; //保存多次调用WriteFile后的最大值,这个例子里面,文件长度只会增长,不会减小。 if (ulOffset + ulLen > pdx->filelen) { pdx->filelen = ulOffset + ulLen; } } Irp->IoStatus.Status = status; //设置IRP的完成状态(成功还是失败) Irp->IoStatus.Information = ulWrite; //驱动实际操作了多少字节 IoCompleteRequest(Irp, IO_NO_INCREMENT); //结束IRP,无需往下面的驱动传递 KdPrint(("Leave HelloWDMWrite, write: %d bytes, offset: %d, input len: %d\n", ulWrite, ulOffset, ulWrite)); return status; } /*********************************************************************** * 函数名称:HelloWDMRead * 功能描述:处理IRP_MJ_READ请求 * 参数列表: fdo 设备对象FDO Irp 对应于IRP_MJ_READ的IRP * 返回 值:返回状态 ***********************************************************************/ NTSTATUS HelloWDMRead(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { KdPrint(("Enter HelloWDMRead\n")); PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); ULONG ulRead = 0;//实际读取的字节数 ULONG ulLen = stack->Parameters.Read.Length;//caller想要读取的字节数 ULONG ulOffset = stack->Parameters.Read.ByteOffset.QuadPart;//caller想要读取的偏移量 NTSTATUS status = STATUS_SUCCESS; if (ulOffset + ulLen > MAX_FILE_LEN) {//超出驱动缓冲边界,读取失败 status = STATUS_FILE_INVALID; ulRead = 0; } else { //将驱动缓冲(模拟的文件)中的内存COPY到AssociatedIrp.SystemBuffer中。 RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, pdx->buffer + ulOffset, ulLen); ulRead = ulLen; status = STATUS_SUCCESS; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = ulRead; IoCompleteRequest(Irp, IO_NO_INCREMENT); //结束IRP,无需往下面的驱动传递 KdPrint(("Leave HelloWDMRead, read: %d bytes, offset: %d, input len: %d\n", ulRead, ulOffset, ulLen)); return status; } /*********************************************************************** * 函数名称:HelloWDMQueryInformation * 功能描述:处理IRP_MJ_QUERY_INFORMATION请求(GetFileSize) * 参数列表: fdo 设备对象FDO Irp 对应于IRP_MJ_QUERY_INFORMATION的IRP * 返回 值:返回状态 ***********************************************************************/ NTSTATUS HelloWDMQueryInformation(IN PDEVICE_OBJECT fdo, IN PIRP Irp) { KdPrint(("Enter HelloWDMQueryInformation\n")); PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION)fdo->DeviceExtension; PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp); FILE_INFORMATION_CLASS info = stack->Parameters.QueryFile.FileInformationClass; NTSTATUS status = STATUS_SUCCESS; //IRP_MJ_QUERY_INFORMATION可以获得不同的信息,GetFileSize触发的IRP_MJ_QUERY_INFORMATION对应的是 //FileStandardInformation。 if (info == FileStandardInformation) {//处理GetFileSize请求 KdPrint(("FileStandardInformation\n")); //获取缓冲区数据 PFILE_STANDARD_INFORMATION file_info = (PFILE_STANDARD_INFORMATION)Irp->AssociatedIrp.SystemBuffer; file_info->EndOfFile = RtlConvertLongToLargeInteger(pdx->filelen);//修改FILE_STANDARD_INFORMATION的EndOfFile属性, } else {//此驱动不处理任何其他的IRP_MJ_QUERY_INFORMATION请求 status = STATUS_FILE_INVALID; } Irp->IoStatus.Status = status; Irp->IoStatus.Information = stack->Parameters.QueryFile.Length; IoCompleteRequest(Irp, IO_NO_INCREMENT); KdPrint(("Leave HelloWDMQueryInformation, operated: %d bytes\n", Irp->IoStatus.Information)); return status; }
用户模式测试程序
简单来测试一些write,read和filesize。
// TestWDMDriver.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <windows.h> #define DEVICE_NAME L"\\\\.\\HelloWDM" int _tmain(int argc, _TCHAR* argv[]) { HANDLE hDevice = CreateFile(DEVICE_NAME,GENERIC_READ | GENERIC_WRITE,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); if (hDevice != INVALID_HANDLE_VALUE) { char buf[] = "hello world"; DWORD dwWrite = 0; BOOL b = WriteFile(hDevice, buf, strlen(buf), &dwWrite, NULL);//写入device printf("WriteFile, ret: %d, wrote bytes: %d, last error: %d\n", b, dwWrite, GetLastError()); DWORD len = GetFileSize(hDevice, NULL);//文件长度 printf("File len: %d\n", len); //再写一次, dwWrite = 0; b = WriteFile(hDevice, buf, strlen(buf), &dwWrite, NULL);//写入device printf("WriteFile, ret: %d, wrote bytes: %d, last error: %d\n", b, dwWrite, GetLastError()); len = GetFileSize(hDevice, NULL); printf("File len: %d\n", len); // dwWrite = 0; OVERLAPPED ol; ol.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); ol.Offset = 6; ol.OffsetHigh = 0; ol.InternalHigh = strlen(buf); b = WriteFile(hDevice, buf, strlen(buf), &dwWrite, &ol);//写入device printf("WriteFile, ret: %d, wrote bytes: %d, last error: %d\n", b, dwWrite, GetLastError()); len = GetFileSize(hDevice, NULL); printf("File len: %d\n", len); char buf2[100] = {0}; //读数据 DWORD dwRead = 0; b = ReadFile(hDevice, buf2, 15, &dwRead, NULL); printf("ReadFile, ret: %d, read content: %s, read len: %d bytes\n", b, buf2, dwRead); CloseHandle(hDevice); } else printf("CreateFile failed, err: %x\n", GetLastError()); return 0; }
调用了3次WriteFile,第一次写了11个字节"hello world"(offset=0),第二次又写了同样的11个字节,因为没有指定offset,所以把第一次写的给覆盖了,第三次使用一个OVERLAPPED结构指定offset,从offset=6开始写11个字节"hello world"。
调用ReadFile读取15个字节,那么应该得到一个字符串:hello hello wor。
打开debugview,看一下log:
驱动输出3个major function code = 4,3次code=5,1次code=3.看一下定义:
#define IRP_MJ_CREATE 0x00 #define IRP_MJ_CREATE_NAMED_PIPE 0x01 #define IRP_MJ_CLOSE 0x02 #define IRP_MJ_READ 0x03 #define IRP_MJ_WRITE 0x04 #define IRP_MJ_QUERY_INFORMATION 0x05
4代表IRP_MJ_WRITE,5代表IRP_MJ_QUERY_INFORMATION,3代表IRP_MJ_READ。看一下测试代码,可以发现调用了
3次WriteFile, 3次GetFileSize和1次ReadFile,和驱动的log一致。
看一下用户模式的输出:
可以看到最后一行log显示读取到了字符串:hello hello wor, 15个字节。file len都是17,这是因为设备扩展里面只保存了最大file size。最后一次写是从offset=6的地方开始写的,那么offset前面还有012345总共6个字节的空间, 6 + 11 = 17,正确。
完整代码下载:http://download.csdn.net/detail/zj510/4798209
DDK 编译驱动,VS2008编译调用例子。