Windows驱动开发WDM (4)- 缓冲区方式例子

将前面的例子扩展一下,这次让这个驱动模拟一个文件,支持读写和获取文件长度。

 

在驱动中创建一块存储,用来模拟保存文件数据

首先给设备扩展增加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;
}


 

主要函数都介绍完了,我这里画了个简单的序列图:

Windows驱动开发WDM (4)- 缓冲区方式例子_第1张图片

 

完整驱动代码

贴出全部的驱动代码,主要就是增加了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:

Windows驱动开发WDM (4)- 缓冲区方式例子_第2张图片

驱动输出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一致。

看一下用户模式的输出:

Windows驱动开发WDM (4)- 缓冲区方式例子_第3张图片

可以看到最后一行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编译调用例子。
 


 

你可能感兴趣的:(Windows驱动开发WDM (4)- 缓冲区方式例子)