以缓冲区方式写设备时,操作系统将WriteFile提供的用户模式的缓冲区复制到内核模式地址下,这个地址由 WriteFile 创建的IRP的 AssociatedIrp.SystemBuffer 子域记录。
以“缓冲区”方式读设备时,操作系统会分配一段内核模式下的内存。这段内存大小等于ReadFile或者WriteFile指定的字节数,并且ReadFile或者WriteFile创建的IRP的AssociatedIrp.SystemBuffer子域会记录这段内存地址。当IRP请求结束时(一般都是由IoCompleteRequest函数结束IRP),这段内存地址会被复制到ReadFile提供的缓冲区中。
以缓冲区方式无论是“读”还是“写”设备,都会发生用户模式地址与内核模式地址的数据复制。复制的过程由操作系统负责。用户模式地址有ReadFile或者WriteFile提供,内核模式地址由操作系统负责分配和回收。
另外,在派遣函数中,也可以通过 IO_STACK_LOCATION 中的 Parameters.Read.Length子域知道ReadFile请求多少字节。通过 IO_STACK_LOCATION 中的 Parameters.Write.Length 知道WriteFile请求多少字节。
然而,WriteFile 和 ReadFile指定对设备操作多少字节,并不真正意味着操作了这么多字节,
在派遣函数中,应该设置IRP的子域 IoStatus.Information。这个子域记录设备实际操作了多少字节。
ReadFile 和 WriteFile 分别通过各自的第四个参数得到真实操作了多少字节。
下面代码演示了如何利用“缓冲区”方式读设备。本例的驱动程序返回给应用程序的数据都是0XAA,因此应用程序会从设备中读到一连串的0XAA。
其中,驱动程序中的派遣函数是这样的:
NTSTATUS HelloDDKRead(IN PDEVICE_OBJECT pDevObj,
IN PIRP pIrp)
{
KdPrint(("Enter HelloDDKRead\n"));
//对一般IRP的简单操作,后面会介绍对IRP更复杂的操作
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
ULONG ulReadLength = stack->Parameters.Read.Length;
// 完成IRP
//设置IRP完成状态
pIrp->IoStatus.Status = status;
//设置IRP操作了多少字节
pIrp->IoStatus.Information = ulReadLength; // bytes xfered
memset(pIrp->AssociatedIrp.SystemBuffer,0xAA,ulReadLength);
//处理IRP
IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKRead\n"));
return status;
}
应用程序使用ReadFile对设备进行读写:
int main()
{
HANDLE hDevice =
CreateFile("\\\\.\\HelloDDK",
GENERIC_READ | GENERIC_WRITE,
0, // share mode none
NULL, // no security
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL ); // no template
if (hDevice == INVALID_HANDLE_VALUE)
{
printf("Failed to obtain file handle to device: "
"%s with Win32 error code: %d\n",
"MyWDMDevice", GetLastError() );
return 1;
}
UCHAR buffer[10];
ULONG ulRead;
BOOL bRet = ReadFile(hDevice,buffer,10,&ulRead,NULL);
if (bRet)
{
printf("Read %d bytes:",ulRead);
for (int i=0;i<(int)ulRead;i++)
{
printf("%02X ",buffer[i]);
}
printf("\n");
}
CloseHandle(hDevice);
return 0;
}
另外,可以用IRPTrace工具查看设备对象对应的IO_STACK_LOCATION结构,从中可以看出,IRP请求是在HelloDDK.sys!HelloDDKRead中被结束的,一共读取10个字节,每个字节都是
0xAA。