应用程序与设备对象交换数据的三种方法

1.缓冲区设备读写
伪代码:
创建设备及指定设备标记
NTSTATUS status;
PDEVICE_OBJECT pDevObj;
PDEVICE_EXTENSION pDevExt;

//创建设备名称
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

//创建设备
status = IoCreateDevice( pDriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0, TRUE,
&pDevObj );
if (!NT_SUCCESS(status))
return status;
//指定设备标记为缓冲区读写
pDevObj->Flags |= DO_BUFFERED_IO;



读取操作
NTSTATUS status = STATUS_SUCCESS;

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//要读取的大小 在IO堆栈的Parameters.Read.Length中
ULONG ulReadLength = stack->Parameters.Read.Length;

pIrp->IoStatus.Status = status;

pIrp->IoStatus.Information = ulReadLength; // bytes xfered
//直接操作pIrp->AssociatedIrp.SystemBuffer即可操作应用的虚拟内存
memset(pIrp->AssociatedIrp.SystemBuffer,0xAA,ulReadLength);

IoCompleteRequest( pIrp, IO_NO_INCREMENT );
return status;
2.直接读写
伪代码:
创建设备及指定设备标记
NTSTATUS status;
PDEVICE_OBJECT pDevObj;
PDEVICE_EXTENSION pDevExt;

//创建设备名称
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

//创建设备
status = IoCreateDevice( pDriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0, TRUE,
&pDevObj );
if (!NT_SUCCESS(status))
return status;
//指定设备的标记为直接读写的方式
pDevObj->Flags |= DO_DIRECT_IO;



读取的代码
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);

ULONG ulReadLength = stack->Parameters.Read.Length;
KdPrint(("ulReadLength:%d\n",ulReadLength));

ULONG mdl_length = MmGetMdlByteCount(pIrp->MdlAddress);
//这里得到的用户模式下 缓冲区的地址 2GB以下的地址
PVOID mdl_address = MmGetMdlVirtualAddress(pIrp->MdlAddress);
ULONG mdl_offset = MmGetMdlByteOffset(pIrp->MdlAddress);

KdPrint(("mdl_address:0X%08X\n",mdl_address));
KdPrint(("mdl_length:%d\n",mdl_length));
KdPrint(("mdl_offset:%d\n",mdl_offset));

if (mdl_length!=ulReadLength)
{
//MDL的长度应该和读长度相等,否则该操作应该设为不成功
pIrp->IoStatus.Information = 0;
status = STATUS_UNSUCCESSFUL;
}else
{
//用MmGetSystemAddressForMdlSafe得到MDL在内核模式下的映射 这里的地址应该属于2G-4G中
PVOID kernel_address = MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority);
KdPrint(("kernel_address:0X%08X\n",kernel_address));
memset(kernel_address,0XAA,ulReadLength);
pIrp->IoStatus.Information = ulReadLength; // bytes xfered
}

pIrp->IoStatus.Status = status;

IoCompleteRequest( pIrp, IO_NO_INCREMENT );
KdPrint(("Leave HelloDDKRead\n"));

return status;
3.其他方式读写
创建设备代码 如果使用这种方式 则不指定任何标记
NTSTATUS status;
PDEVICE_OBJECT pDevObj;
PDEVICE_EXTENSION pDevExt;

//创建设备名称
UNICODE_STRING devName;
RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");

//创建设备
status = IoCreateDevice( pDriverObject,
sizeof(DEVICE_EXTENSION),
&(UNICODE_STRING)devName,
FILE_DEVICE_UNKNOWN,
0, TRUE,
&pDevObj );
if (!NT_SUCCESS(status))
return status;



写入代码
PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
NTSTATUS status = STATUS_SUCCESS;

//得到当前堆栈
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp);
//得到读的长度
ULONG ulReadLength = stack->Parameters.Read.Length;
//得到读的偏移量
ULONG ulReadOffset = (ULONG)stack->Parameters.Read.ByteOffset.QuadPart;
//得到用户模式地址
PVOID user_address = pIrp->UserBuffer;

KdPrint(("user_address:0X%0X\n",user_address));

__try
{
KdPrint(("Enter __try block\n"));
//判断空指针是否可写,显然会导致异常
ProbeForWrite(user_address,ulReadLength,4);

memset(user_address,0xAA,ulReadLength);

//由于在上面引发异常,所以以后语句不会被执行!
KdPrint(("Leave __try block\n"));
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
KdPrint(("Catch the exception\n"));
KdPrint(("The program will keep going\n"));
status = STATUS_UNSUCCESSFUL;
}

pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = ulReadLength; // bytes xfered
IoCompleteRequest( pIrp, IO_NO_INCREMENT );

return status;

posted @ 2015-07-02 10:00  银翼的魔术师  阅读(447)  评论(0编辑  收藏  举报