用户层与驱动层通信

虚幻大学 xuhss 136℃ 0评论

Python微信订餐小程序课程视频

https://edu.csdn.net/course/detail/36074

Python实战量化交易理财系统

https://edu.csdn.net/course/detail/35475
备注:1.在下面的讨论中,"输入"表示数据从用户模式的应用程序到驱动程序,"输出"表示数据从驱动程序到应用程序。2.IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。

参考文章链接:https://blog.csdn.net/endenvor/p/9057856.html/ ,个人觉得原文有些乱,并且代码不完整,在此做了整理和补充。


一、I/O设备控制操作

R3通过DeviceControl函数来写入和读取R0的数据。R0层通过一下几种方式读取R3发送的数据和向R3发送数据。

(1)"缓冲"内存IOCTL -- METHOD_BUFFERED

  对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。

  对于 IOCTL,驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。(ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理,就是对设备的一些特性进行控制,例如串口的传输波特率、马达的转速等等。)

  输入缓冲区大小:stack->Parameters.DeviceIoControl.InputBufferLength  输出缓冲区大小:stack->Parameters.DeviceIoControl.OutputBufferLength  输入缓冲区:pIrp->AssociatedIrp.SystemBuffer  输出缓冲区:pIrp->AssociatedIrp.SystemBuffer

2)"直接"方法IOCTL -- METHOD_IN_DIRECT/METHOD_OUT_DIRECT

  对于读取和写入请求,用户模式缓冲区会被锁定,并且会重新映射一个内存描述符列表 (MDL)。MDL 地址会存储在 IRP 的 MdlAddress 字段中。根据 Irp->MdlAddress 重新为这片物理地址映射一份虚拟地址。通过这份虚拟地址向R3层发送数据。

  SystemBuffer 和 UserBuffer 均没有任何含义。但是,驱动程序不应当更改这些字段的值。

  输入缓冲区大小:stack->Parameters.DeviceIoControl.InputBufferLength  输出缓冲区大小:stack->Parameters.DeviceIoControl.OutputBufferLength  输入缓冲区:MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority)**输出缓冲区:pIrp->AssociatedIrp.SystemBuffer**

  METHOD_IN_DIRECT/METHOD_OUT_DIRECT区别:     1) 只读权限打开设备,METHOD_IN_DIRECT的IOCTL操作成功,而METHOD_OUT_DIRECT的IOCTL操作失败     2) 读写权限打开设备,METHOD_IN_DIRECT与METHOD_OUT_DIRECT的IOCTL操作都成功

(3)"两种都不"方法IOCTL(其他内存模式) -- MEHTOD_NEITHER

  对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。

  输入缓冲区大小:stack->Parameters.DeviceIoControl.InputBufferLength  输出缓冲区大小:stack->Parameters.DeviceIoControl.OutputBufferLength  输入缓冲区:ProbeForRead(stack->Parameters.DeviceIoControl.Type3InputBuffer)**输出缓冲区:ProbeForWrite(pIrp->UserBuffer)**


二、读写操作

  分为一下三种,ReadFile,WirteFile方式的缓冲区设备读写,直接方式读写,和其他方式读写(DO_BUFFERED_IO、DO_DIRECT_IO、METHOD_NEITHER)。

  在R3层通过 ReadFile,WriteFile 函数进行读写数据,在R0层通过 IRP_MJ_READ 与 IRP_MJ_WRITE 对应的派遣函数中处理以下三种缓存区来进行数据读写。

(1)缓冲区方式读写 -- DO_BUFFERED_IO

  在创建 Device 后,须要指定方式为 Device 的 Flags 有 DO_BUFFERED_IO。通过应用层 Api 函数 ReadFile,WriteFile 等函数。ntoskrnl.exe创建Irp后,ReadFile和WriteFile参数的缓冲区就在irp->AssociatedIrp.Systembuffer。

  同时要求读写的偏移量,和长度都在 PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp) 数据类型中的stack->Parameters.Read.Length,stack->Parameters.Read.ByteOffset(该类型为Large_Interge类型)。

  对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区 SystemBuffer。当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。

  对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。

  ReadFile /WriteFile   读取/写入字节数:stack->Parameters.Read.Length    偏移:stack->Parameters.Read.ByteOffset.QuadPart输出/写入缓冲区:pIrp->AssociatedIrp.SystemBuffer    输出/返回字节数:pIrp->IoStatus.Information

  IRP_MJ_QUERY_INFORMATION   FILE_INFORMATION_CLASS: stack->Parameters.QueryFile.FileInformation    输入输出缓冲区:pIrp->AssociatedIrp.SystemBuffer    返回字节数:pIrp->IoStatus.Information=stack->Parameters.QueryFile.Length

2)直接方式读写 -- DO_DIRECT_IO

  在创建 Device 后,须要指定方式为 Device 的 Flags 有 DO_DIRECT_IO 。通过应用层 APi 函数 ReadFile,WriteFile 等函数,ntoskrnl.exe创建的Irp后,ReadFile 和 WriteFile 参数的缓冲区将被锁住,然后操作系统将这段缓冲区在内核模式地址再次映射一遍,这样应用层的缓冲区和内存层的就指向同一个物理内存。而内核模式用MDL数据结构记录这段内存,这个虚拟内存大小在MmGetByteCount(pIrp->MdlAddress),首地址在MmGetMdlVirtualAddress(pIrp->MdlAddress);

  偏移量为MmGetMdlByteOffset(pIrp->MdlAddress)(这里的偏移量不是文件读写的偏移量,而是在MDL中的偏移量),然后文件的长度还是stack->Parameters.Read.Length,这个值和MmGetByteCount(pIrp->MdlAddress)是一样的,要不然就出错了,而真正的读写偏移量还是在stack->Parameters.Read.ByteOffset。

  这里无论是读还是写,都要得到MDL在内核模式下的映射,因此还要用MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority)在内核中根据 pIrp->MdlAddress 重新为其对应的物理地址映射一份虚拟地址,然后可以读写该地址,就会转化到应用层相应的内存。

  读取/写入字节数:stack->Parameters.Read.Length  偏移:stack->Parameters.Read.ByteOffset.QuadPart  输出/写入缓冲区:MmGetSystemAddressForMdlSafe(pIrp->MdlAddress,NormalPagePriority)  输出/返回字节数:pIrp->IoStatus.Information

(3)其他方式读写 -- METHOD_NEITHER

  在创建 Device 后,Flags 既不标志 DO_BUFFERED_IO 也不标志 DO_DIRECT_IO,ReadFile 和 WriteFile 提供的缓冲区内存地址,可以再 IRP 的 pIrp->UserBuffer 字段得到,而长度和偏移量还是在 stack->Paameters.Read 中。

  但是用这种方法须要注意的是 ReadFile 可能把空指针地址或者非法地址传递给驱动程序,因此驱动程序使用用户模式地址钱须要检查是否可读或者可写,可以用 ProbeForWrite 或者 ProbeForWrite 函数和try模块。

  对于写请求,UserBuffer 字段被设置为指向输出缓冲区。

  对于读请求,Type3InputBuffer 字段被设置为输入缓冲区。不执行任何其他操作。

  SystemAddress 和 MdlAddress 没有任何含义。

  读取/写入字节数:stack->Parameters.Read.Length  偏移:stack->Parameters.Read.ByteOffset.QuadPart输出/写入缓冲区:ProbeForWrite(pIrp->UserBuffer)  输出/返回字节数:pIrp->IoStatus.Information


R0层代码:

#include 

#define CTRL\_CODE1 CTL\_CODE(FILE\_DEVICE\_UNKNOWN, 0x801, METHOD\_IN\_DIRECT, FILE\_ANY\_ACCESS) // "直接"方法
#define CTRL\_CODE2 CTL\_CODE(FILE\_DEVICE\_UNKNOWN, 0x802, METHOD\_BUFFERED, FILE\_ANY\_ACCESS) // "缓冲"方法 
#define CTRL\_CODE3 CTL\_CODE(FILE\_DEVICE\_UNKNOWN, 0x803, METHOD\_NEITHER, FILE\_ANY\_ACCESS) // "两者都不"方法

// 创建一个设备对象
NTSTATUS CreateDevice(PDRIVER\_OBJECT DriverObject, PUNICODE\_STRING DeviceName, PUNICODE\_STRING SymbolName, UINT32 DO)
{
 PDEVICE\_OBJECT DeviceObject = NULL;

 // 1. 创建一个设备对象
 NTSTATUS Status = IoCreateDevice(DriverObject, 0, DeviceName,
 FILE\_DEVICE\_UNKNOWN, 0, FALSE, &DeviceObject);

 if (NT\_SUCCESS(Status))
 {
 // 2. 添加符号链接名
 Status = IoCreateSymbolicLink(SymbolName, DeviceName);

 // 3. 设置设备的通讯方式
 DeviceObject->Flags |= DO;
 }

 // 4. 设置返回值
 return Status;
}

NTSTATUS DefaultProc(struct \_DEVICE\_OBJECT *DeviceObject, struct \_IRP *Irp)
{
 UNREFERENCED\_PARAMETER(DeviceObject);

 // 1. 设置处理的字节数量
 Irp->IoStatus.Information = 0;
 // 2. 设置状态
 Irp->IoStatus.Status = STATUS\_SUCCESS;
 // 3. 表示已完成所有IRP处理
 IoCompleteRequest(Irp, IO\_NO\_INCREMENT);
 // 4. 设置返回值
 return STATUS\_SUCCESS;
}

// 不设置这个函数,则Ring3调用CreateFile会返回1
// IRP\_MJ\_CREATE 处理函数
NTSTATUS CreateProc(struct \_DEVICE\_OBJECT *DeviceObject, struct \_IRP *Irp)
{
 UNREFERENCED\_PARAMETER(DeviceObject);

 // 1. 设置处理的字节数量
 Irp->IoStatus.Information = 0;
 // 2. 设置状态
 Irp->IoStatus.Status = STATUS\_SUCCESS;
 // 3. 表示已完成所有IRP处理
 IoCompleteRequest(Irp, IO\_NO\_INCREMENT);
 // 4. 设置返回值
 return Irp->IoStatus.Status;
}

/*******************************缓冲区方式读写操作*********************************/

NTSTATUS WriteProc(struct \_DEVICE\_OBJECT *DeviceObject, struct \_IRP *Irp)
{
 UNREFERENCED\_PARAMETER(DeviceObject);

 PIO\_STACK\_LOCATION pstack = IoGetCurrentIrpStackLocation(Irp);
 ULONG WriteSize = pstack->Parameters.Write.Length;

 char *Buffer = NULL;
 // 判断读写方式
 if (Irp->AssociatedIrp.SystemBuffer) // DO\_BUFFERED\_IO 缓冲方法
 Buffer = Irp->AssociatedIrp.SystemBuffer;
 else if (Irp->MdlAddress) // DO\_DIRECT\_IO 直接方法
 Buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
 else if (Irp->UserBuffer) // METHOD\_NEITHER 方法
 Buffer = Irp->UserBuffer;

 // 对该地址区域写入一些数据,让程序读出去
 KdPrint(("缓冲区方式读写操作 - 来自R3的数据:%S\n", Buffer));

 Irp->IoStatus.Information = WriteSize;
 Irp->IoStatus.Status = STATUS\_SUCCESS;
 IoCompleteRequest(Irp, IO\_NO\_INCREMENT);
 return STATUS\_SUCCESS;
}

NTSTATUS ReadProc(struct \_DEVICE\_OBJECT *DeviceObject, struct \_IRP *Irp)
{
 UNREFERENCED\_PARAMETER(DeviceObject);

 char *Buffer = NULL;
 // 判断读写方式

 if (Irp->AssociatedIrp.SystemBuffer) // DO\_BUFFERED\_IO 缓冲方法
 Buffer = Irp->AssociatedIrp.SystemBuffer;
 else if (Irp->MdlAddress) // DO\_DIRECT\_IO 直接方法
 Buffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
 else if (Irp->UserBuffer) // METHOD\_NEITHER 方法
 Buffer = Irp->UserBuffer;

 // 对该地址区域写入一些数据,让程序读出去
 RtlCopyMemory(Buffer, L"Hello 15PB", 22);

 Irp->IoStatus.Information = 22;
 Irp->IoStatus.Status = STATUS\_SUCCESS;
 IoCompleteRequest(Irp, IO\_NO\_INCREMENT);
 return STATUS\_SUCCESS;
}

/*******************************IO设备控制操作**********************************/

NTSTATUS DeviceIoCtrlProc(struct \_DEVICE\_OBJECT *DeviceObject, struct \_IRP *Irp)
{
 UNREFERENCED\_PARAMETER(DeviceObject);

 PIO\_STACK\_LOCATION pStack = IoGetCurrentIrpStackLocation(Irp);
 ULONG CtrlCode = pStack->Parameters.DeviceIoControl.IoControlCode;
 ULONG OutputLength = pStack->Parameters.DeviceIoControl.OutputBufferLength;
 ULONG Information = 0;

 switch (CtrlCode)
 {
 // "直接"方法
 case CTRL\_CODE1:
 {
 // 输入缓冲I/O方法,来自R3的数据打印
 char *SystemBuffer = Irp->AssociatedIrp.SystemBuffer;
 KdPrint(("IO设备控制操作 - 缓冲方式 - 来自R3的数据:%S\n", SystemBuffer)); 
 Information = 64;

 // 直接输出
 // MDL 地址会存储在 IRP 的 MdlAddress 字段中。根据 Irp->MdlAddress 重新为这片物理地址映射一份虚拟地址。
 // MmGetSystemAddressForMdlSafe是一个宏,它为MDL描述的缓冲区返回一个非分页的系统空间虚拟地址。
 char *MdlBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);
 // Irp->MdlAddress:0xfffffa80`1afe6010
 RtlCopyMemory(MdlBuffer, L"CTRL\_CODE1 - METHOD\_IN\_DIRECT\n", Information);
 // MdlBuffer:0xfffff880`0bf968f0
 break;
 }

 // 输入输出缓冲I/O方法
 case CTRL\_CODE2:
 {
 // IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。

 // 对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。
 // 当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。

 // 对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。
 // 用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。
 char *Buffer = Irp->AssociatedIrp.SystemBuffer;
 KdPrint(("IO设备控制操作 - 缓冲方式 - 来自R3的数据:%S\n", Buffer)); // 输入缓冲I/O方法,来自R3的数据打印
 Information = 62;
 RtlCopyMemory(Buffer, L"CTRL\_CODE2 - METHOD\_BUFFERED\n", Information); // 输出缓冲I/O方法,R0传送数据
 break;
 }

 // "两种都不"方法
 case CTRL\_CODE3:
 {
 // 对于写请求,UserBuffer 字段被设置为指向输出缓冲区。
 // 对于读请求,Type3InputBuffer 字段被设置为输入缓冲区。
 // 不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。
 char *InBuffer = pStack->Parameters.DeviceIoControl.Type3InputBuffer;
 char *OutBuffer = Irp->UserBuffer;
 KdPrint(("IO设备控制操作 - 两种都不方式 - 来自R3的数据:%S\n", InBuffer)); // "两种都不"方法,来自R3的数据打印
 Information = 60;
 RtlCopyMemory(OutBuffer, L"CTRL\_CODE3 - METHOD\_NEITHER\n", Information);
 break;
 }
 default:
 KdPrint(("failed!\n"));
 break;
 }

 // 1. 设置处理的字节数量
 Irp->IoStatus.Information = OutputLength;
 // 2. 设置状态
 Irp->IoStatus.Status = STATUS\_SUCCESS;
 // 3. 表示已完成所有IRP处理
 IoCompleteRequest(Irp, IO\_NO\_INCREMENT);
 // 4. 设置返回值
 return STATUS\_SUCCESS;
}

// 卸载函数
void DriverUnload(PDRIVER\_OBJECT DriverObject)
{
 // 获取设备对象链表
 PDEVICE\_OBJECT DeviceObject = DriverObject->DeviceObject;
 for (int i = 0; i < 3; ++i)
 {
 IoDeleteDevice(DriverObject->DeviceObject);
 DeviceObject = DeviceObject->NextDevice;
 }

 // 卸载设备符号名
 UNICODE\_STRING SymbolLinkName1 = RTL\_CONSTANT\_STRING(L"\\DosDevices\\devicelink1");
 UNICODE\_STRING SymbolLinkName2 = RTL\_CONSTANT\_STRING(L"\\DosDevices\\devicelink2");
 UNICODE\_STRING SymbolLinkName3 = RTL\_CONSTANT\_STRING(L"\\DosDevices\\devicelink3");
 IoDeleteSymbolicLink(&SymbolLinkName1);
 IoDeleteSymbolicLink(&SymbolLinkName2);
 IoDeleteSymbolicLink(&SymbolLinkName3);
}

NTSTATUS DriverEntry(PDRIVER\_OBJECT DriverObject, PUNICODE\_STRING UnicodeString)
{
 //DbgBreakPoint();

 UNREFERENCED\_PARAMETER(UnicodeString);

 // 创建不同读写方式的设备
 UNICODE\_STRING DeviceName1 = RTL\_CONSTANT\_STRING(L"\\Device\\device1");
 UNICODE\_STRING SymbolName1 = RTL\_CONSTANT\_STRING(L"\\DosDevices\\devicelink1");
 if (NT\_SUCCESS(CreateDevice(DriverObject, &DeviceName1, &SymbolName1, DO\_BUFFERED\_IO)))
 KdPrint(("Create device success: \\Device\\device1\n"));

 UNICODE\_STRING DeviceName2 = RTL\_CONSTANT\_STRING(L"\\Device\\device2");
 UNICODE\_STRING SymbolName2 = RTL\_CONSTANT\_STRING(L"\\DosDevices\\devicelink2");
 if (NT\_SUCCESS(CreateDevice(DriverObject, &DeviceName2, &SymbolName2, DO\_DIRECT\_IO)))
 KdPrint(("Create device success: \\Device\\device2\n"));

 UNICODE\_STRING DeviceName3 = RTL\_CONSTANT\_STRING(L"\\Device\\device3");
 UNICODE\_STRING SymbolName3 = RTL\_CONSTANT\_STRING(L"\\DosDevices\\devicelink3");
 if (NT\_SUCCESS(CreateDevice(DriverObject, &DeviceName3, &SymbolName3, 0)))
 KdPrint(("Create device success: \\Device\\device3\n"));

 // 设置卸载函数
 DriverObject->DriverUnload = DriverUnload;

 // 初始化IRP派遣函数
 for (UINT32 i = 0; i < IRP\_MJ\_MAXIMUM\_FUNCTION; i++)
 DriverObject->MajorFunction[i] = DefaultProc;
 DriverObject->MajorFunction[IRP\_MJ\_CREATE] = CreateProc;
 DriverObject->MajorFunction[IRP\_MJ\_READ] = ReadProc;
 DriverObject->MajorFunction[IRP\_MJ\_WRITE] = WriteProc;
 DriverObject->MajorFunction[IRP\_MJ\_DEVICE\_CONTROL] = DeviceIoCtrlProc;

 return STATUS\_SUCCESS;
}

R3层代码:

#include <cstdio>
#include <windows.h>
#include <tchar.h>
#define CTRL\_CODE1 CTL\_CODE(FILE\_DEVICE\_UNKNOWN, 0x801, METHOD\_IN\_DIRECT, FILE\_ANY\_ACCESS)    // "直接"方法
#define CTRL\_CODE2 CTL\_CODE(FILE\_DEVICE\_UNKNOWN, 0x802, METHOD\_BUFFERED, FILE\_ANY\_ACCESS)    // "缓冲"方法 
#define CTRL\_CODE3 CTL\_CODE(FILE\_DEVICE\_UNKNOWN, 0x803, METHOD\_NEITHER, FILE\_ANY\_ACCESS)    // "两者都不"方法

int main()
{
    \_tsetlocale(0, \_T("Chinese-simplified")); //支持中文

    // 创建内核对象的设备对象连接 --- 对应驱动层中 IRP\_MJ\_WRITE 请求的过滤函数
    HANDLE DeviceHandle1 = CreateFile(L"\\\\.\\devicelink1", FILE\_ALL\_ACCESS,
        FILE\_SHARE\_WRITE | FILE\_SHARE\_READ, NULL, OPEN\_EXISTING,
        FILE\_ATTRIBUTE\_NORMAL, NULL);
    HANDLE DeviceHandle2 = CreateFile(L"\\\\.\\devicelink2", FILE\_ALL\_ACCESS,
        FILE\_SHARE\_WRITE | FILE\_SHARE\_READ, NULL, OPEN\_EXISTING,
        FILE\_ATTRIBUTE\_NORMAL, NULL);
    HANDLE DeviceHandle3 = CreateFile(L"\\\\.\\devicelink3", FILE\_ALL\_ACCESS,
        FILE\_SHARE\_WRITE | FILE\_SHARE\_READ, NULL, OPEN\_EXISTING,
        FILE\_ATTRIBUTE\_NORMAL, NULL);

    WCHAR ReadBuffer[100] = { 0 };
    DWORD ReadByte = 0;
    DWORD WriteByte = 0;

    /**********************************************************************************************/

    // 使用WriteFile、ReadFile函数传送数据 --- 对应驱动层中 IRP\_MJ\_CREATE 与 IRP\_MJ\_READ 请求的过滤函数
    // 根据驱动中Device创建时所选择的通信缓冲方法,进行数据传送
    wprintf(L"缓冲区方式读写操作:\n");
    printf("(1)device1 - DO\_BUFFERED\_IO:\n");
    WriteFile(DeviceHandle1, L"device1 - DO\_BUFFERED\_IO", 52, &WriteByte, NULL);    // DeviceHandle1 创建时选择了 DO\_BUFFERED\_IO 缓冲方法
    printf("\tWriteBytes: %d\n", WriteByte);
    ReadFile(DeviceHandle1, ReadBuffer, 100, &ReadByte, NULL);
    wprintf(L"\tRead: %d(%s)\n\n", ReadByte, ReadBuffer);    // 打印出22,这是对的

    printf("(2)device2 - DO\_DIRECT\_IO:\n");
    WriteFile(DeviceHandle2, L"device2 - DO\_DIRECT\_IO", 48, &WriteByte, NULL);        // DeviceHandle2 创建时选择了 DO\_DIRECT\_IO 直接方法
    printf("\tWriteBytes: %d\n", WriteByte);
    ReadFile(DeviceHandle2, ReadBuffer, 100, &ReadByte, NULL);
    wprintf(L"\tRead: %d(%s)\n\n", ReadByte, ReadBuffer);    // 打印出22,这是对的

    printf("这个有BUG\n");
    printf("(3)device3 - METHOD\_NEITHER:\n");
    WriteFile(DeviceHandle3, L"device3 - METHOD\_NEITHER", 32, &WriteByte, NULL);    // DeviceHandle3 创建时选择了 METHOD\_NEITHER 方法
    printf("\tdevice3 - Write: %d\n", WriteByte);
    ReadFile(DeviceHandle3, ReadBuffer, 100, &ReadByte, NULL);
    printf("\tdevice3 - Read: %d(%Ls)\n\t", ReadByte, ReadBuffer); // 打印出22,有bug??

    /**********************************************************************************************/

    // 使用DeviceIoControl函数传送数据 --- 对应驱动层中Write与Read的过滤函数
    wprintf(L"\n\nIO设备控制操作:\n");
    DeviceIoControl(DeviceHandle1, CTRL\_CODE1, (LPVOID)L"CTRL\_CODE1", 24, ReadBuffer, 100, &ReadByte, NULL);    // "直接"方法
    wprintf(L"(1)直接方法: \n\t ReadByte - ReadBuffer: %d - %s\n", ReadByte, ReadBuffer);

    DeviceIoControl(DeviceHandle1, CTRL\_CODE2, (LPVOID)L"CTRL\_CODE2", 24, ReadBuffer, 100, &ReadByte, NULL);    // 输入输出缓冲I/O方法
    wprintf(L"(2)输入输出缓冲I/O方法: \n\t ReadByte - ReadBuffer: %d - %s\n", ReadByte, ReadBuffer);

    DeviceIoControl(DeviceHandle1, CTRL\_CODE3, (LPVOID)L"CTRL\_CODE3", 24, ReadBuffer, 100, &ReadByte, NULL);    // 上面方法都不是(METHOD\_NEITHER)
    wprintf(L"(3)METHOD\_NEITHER方法: \n\t ReadByte - ReadBuffer: %d - %s\n", ReadByte, ReadBuffer);
    system("pause");

    return 0;
}

转载请注明:xuhss » 用户层与驱动层通信

喜欢 (0)

您必须 登录 才能发表评论!