将前面的例子扩展一下,这次让这个驱动模拟一个文件,支持读写和获取文件长度。
在驱动中创建一块存储,用来模拟保存文件数据
首先给设备扩展增加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;
说明:
Members
-
AllocationSize
- The file allocation size in bytes. Usually, this value is a multiple of the sector or cluster size of the underlying physical device. EndOfFile
- The end of file location as a byte offset. NumberOfLinks
- The number of hard links to the file. DeletePending
- The delete pending status. TRUE indicates that a file deletion has been requested. Directory
- The file directory status. TRUE indicates the file object represents a directory.
其中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编译调用例子。