驱动程序和客户应用程序经常需要进行数据交换,但我们知道驱动程序和客户应用程序可能不在同一个地址空间,因此操作系统必须解决两者之间的数据交换。
驱动层和应用层通信,主要是靠DeviceIoControl函数,下面是该函数的原型:

BOOL DeviceIoControl ( HANDLE hDevice, // 设备句柄 DWORD dwIoControlCode, // IOCTL请求操作代码 LPVOID lpInBuffer, // 输入缓冲区地址 DWORD nInBufferSize, // 输入缓冲区大小 LPVOID lpOutBuffer, // 输出缓冲区地址 DWORD nOutBufferSize, // 输出缓冲区大小 LPDWORD lpBytesReturned, // 存放返回字节数的指针 LPOVERLAPPED lpOverlapped // 用于同步操作的Overlapped结构体指针
);

dwIoControlCode
要进行操作的控制码。驱动程序可以通过CTL_CODE宏来组合定义一个控制码,并在IRP_MJ_DEVICE_CONTROL的实现中进行控制码的操作。在驱动层,irpStack->Parameters.DeviceIoControl.IoControlCode表示了这个控制码。

IOCTL请求有四种缓冲策略,下面一一介绍。 
1、 输入输出缓冲I/O(METHOD_BUFFERED)
2、 直接输入缓冲输出I/O(METHOD_IN_DIRECT)
3、 缓冲输入直接输出I/O(METHOD_OUT_DIRECT)
4、 上面三种方法都不是(METHOD_NEITHER)
(因为METHOD_IN_DIRECT  和 METHOD_OUT_DIRECT  是类似的,所以也可以说是三种。)
为了对这些类型更详细的描述,请看msdn上的解释,我抄录如下:

"缓冲"方法(METHOD_BUFFERED)
备注:在下面的讨论中,"输入"表示数据从用户模式的应用程序到驱动程序,"输出"表示数据从驱动程序到应用程序。

对于读取请求,I/O 管理器分配一个与用户模式的缓冲区大小相同的系统缓冲区。IRP 中的 SystemBuffer 字段包含系统地址。UserBuffer 字段包含初始的用户缓冲区地址。当完成请求时,I/O 管理器将驱动程序已经提供的数据从系统缓冲区复制到用户缓冲区。对于写入请求,会分配一个系统缓冲区并将 SystemBuffer 设置为地址。用户缓冲区的内容会被复制到系统缓冲区,但是不设置 UserBuffer。对于 IOCTL 请求,会分配一个容量大小足以包含输入缓冲区或输出缓冲区的系统缓冲区,并将 SystemBuffer 设置为分配的缓冲区地址。输入缓冲区中的数据复制到系统缓冲区。UserBuffer 字段设置为用户模式输出缓冲区地址。内核模式驱动程序应当只使用系统缓冲区,且不应使用 UserBuffer 中存储的地址。

对于 IOCTL,驱动程序应当从系统缓冲区获取输入并将输出写入到系统缓冲区。当完成请求时,I/O 系统将输出数据从系统缓冲区复制到用户缓冲区。

"直接"方法(METHOD_IN/OUT_DIRECT)
对于读取和写入请求,用户模式缓冲区会被锁定,并且会创建一个内存描述符列表 (MDL)。MDL 地址会存储在 IRP 的 MdlAddress 字段中。SystemBuffer 和 UserBuffer 均没有任何含义。但是,驱动程序不应当更改这些字段的值。

对于 IOCTL 请求,如果在 METHOD_IN_DIRECT 和 METHOD_OUT_DIRECT 中同时有一个输出缓冲区,则分配一个系统缓冲区(SystemBuffer 又有了地址)并将输入数据复制到其中。如果有一个输出缓冲区,且它被锁定,则会创建 MDL 并设置 MdlAddress。UserBuffer 字段没有任何含义。

"两者都不"方法(METHOD_NEITHER)
对于读取和写入请求,UserBuffer 字段被设置为指向初始的用户缓冲区。不执行任何其他操作。SystemAddress 和 MdlAddress 没有任何含义。对于 IOCTL 请求,I/O 管理器将 UserBuffer 设置为初始的用户输出缓冲区,而且,它将当前 I/O 栈位置的 Parameters.DeviceIoControl.Type3InputBuffer 设置为用户输入缓冲区。利用该 I/O 方法,由驱动程序来确定如何处理缓冲区:分配系统缓冲区或创建 MDL。

通常,驱动程序在访问用户数据时不应当将 UserBuffer 字段用作地址,即使当用户缓冲区被锁定时也是如此。这是由于在调用驱动程序时,在系统中可能看不到调用用户的地址空间。(对于该规则的一个例外是,在最高层驱动程序将 IRP 向下传递到较低层的驱动程序之前,它可能需要使用 UserBuffer 来复制数据。)如果使用"直接"或"两者都不"方法,在创建 MDL 之后,驱动程序可以使用 MmGetSystemAddressForMdl 函数来获取有效的系统地址以访问用户缓冲区。

在驱动层,依传输类型的不同,输入缓冲区的位置亦不同,见下表。
传输类型                               位置
METHOD_IN_DIRECT                irp->AssociatedIrp.SystemBuffer
METHOD_OUT_DIRECT             irp->AssociatedIrp.SystemBuffer
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                   irpStack->Parameters.DeviceIoControl.Type3InputBuffer

在驱动层,依传输类型的不同,输出缓冲区的位置亦不同,见下表。
传输类型                              位置
METHOD_IN_DIRECT                irp->MdlAddress
METHOD_OUT_DIRECT             irp->MdlAddress
METHOD_BUFFERED                 irp->AssociatedIrp.SystemBuffer
METHOD_NEITHER                    irp->UserBuffer

所以只要确定了传输方式后,就可以根据各自的位置来读取和写入数据,从而实现应用层和驱动的通信。

下面看驱动层对ioctl控制码的处理代码:

//METHOD_OUT_DIREC方式
NTSTATUS COMM_DirectOutIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{NTSTATUS status = STATUS_UNSUCCESSFUL;PVOID pInputBuffer, pOutputBuffer;ULONG  outputLength, inputLength;DbgPrint("COMM_DirectOutIo\r\n");outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;pInputBuffer = Irp->AssociatedIrp.SystemBuffer;pOutputBuffer = NULL;if(Irp->MdlAddress)pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);if(pInputBuffer && pOutputBuffer){                                                          DbgPrint("COMM_DirectOutIo UserModeMessage = '%s'", pInputBuffer);RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);*sizeofWrite = outputLength;status = STATUS_SUCCESS;}return status;
}// METHOD_IN_DIRECT
NTSTATUS COMM_DirectInIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{NTSTATUS status = STATUS_UNSUCCESSFUL;PVOID pInputBuffer, pOutputBuffer;ULONG  outputLength, inputLength;DbgPrint("COMM_DirectInIo\r\n");outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;pInputBuffer = Irp->AssociatedIrp.SystemBuffer;pOutputBuffer = NULL;if(Irp->MdlAddress)pOutputBuffer = MmGetSystemAddressForMdlSafe(Irp->MdlAddress, NormalPagePriority);if(pInputBuffer && pOutputBuffer){                                                          DbgPrint("COMM_DirectInIo UserModeMessage = '%s'", pInputBuffer);RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);*sizeofWrite = outputLength;status = STATUS_SUCCESS;}return status;
}// METHOD_BUFFERED
NTSTATUS COMM_BufferedIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{NTSTATUS status = STATUS_UNSUCCESSFUL;PVOID pInputBuffer, pOutputBuffer;ULONG  outputLength, inputLength;DbgPrint("COMM_BufferedIo\r\n");outputLength = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;inputLength  = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;pInputBuffer = Irp->AssociatedIrp.SystemBuffer;pOutputBuffer = Irp->AssociatedIrp.SystemBuffer;if(pInputBuffer && pOutputBuffer){              DbgPrint("COMM_BufferedIo UserModeMessage = '%s'", pInputBuffer);RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);*sizeofWrite = outputLength;status = STATUS_SUCCESS;}return status;
}// METHOD_NEITHER
NTSTATUS COMM_NeitherIo(PIRP Irp, PIO_STACK_LOCATION pIoStackIrp, UINT *sizeofWrite)
{NTSTATUS status = STATUS_UNSUCCESSFUL;PVOID pInputBuffer, pOutputBuffer;ULONG  outputLength, inputLength;DbgPrint("COMM_NeitherIo\r\n");outputLength  = pIoStackIrp->Parameters.DeviceIoControl.OutputBufferLength;inputLength   = pIoStackIrp->Parameters.DeviceIoControl.InputBufferLength;pInputBuffer  = pIoStackIrp->Parameters.DeviceIoControl.Type3InputBuffer;pOutputBuffer = Irp->UserBuffer;if(pInputBuffer && pOutputBuffer){              DbgPrint("COMM_NeitherIo UserModeMessage = '%s'", pInputBuffer);RtlCopyMemory(pOutputBuffer, pInputBuffer, outputLength);*sizeofWrite = outputLength;status = STATUS_SUCCESS;}return status;
}代码比较简单,都是取得输入的数据,然后把数据直接拷贝到输出,传输给应用层。应用层的代码:
procedure TfrmMain.Send_Recv_Data(AInData: String; var AOutData:String;IoctlCode: DWORD);
vardwReturn: DWORD;inData:array[0..1023] of char;outData:array[0..1023] of char;
beginStrPCopy(inData, AInData);if m_hCommDevice <> 0 thenbeginDeviceIoControl(m_hCommDevice, IoctlCode, @inData,  Length(inData), @outData, Length(outData), dwReturn, nil);AOutData := StrPas(@outData);end;
end;

上面是进行发送和接受的过程。
需要通信,只要如下做:

代码:

procedure TfrmMain. btnDirect_IN_IOClick (Sender: TObject);
varoutData:String;
beginSend_Recv_Data(Trim(edtDirect_in_in.Text), outData, IOCTL_COMM_DIRECT_IN_IO);edtDirect_in_out.Text := outData;
end;

这是 direct_in方式通信,其他通信方式类似,大家可以参考代码了,这里就不列举了,由于代码比较简单,我就不多说了,大家还是看代码吧,很好明白。最后,给个测试图:

应用层:

驱动层:

windows 驱动和应用层的三种通信方式 r3到r0 DeviceIoControl 4种ioctl请求相关推荐

  1. windows驱动快速入门

    本公众号分享的所有技术仅用于学习交流,请勿用于其他非法活动,如有错漏,欢迎留言交流指正 NT驱动框架 <Windows 内核情景分析>.(毛德操) 前置知识 R3和R0 的由来 Intel ...

  2. 基于Linux的USB主/从设备之间的三种通信方式-转

    随着简单易用的接口日益流行,在中添加对USB接口的支持已成为大势所趋.本文通过介绍中支持USB的各种模块和库,分析了在Linux上利用USB实现高速串口和以太网连接等通信方式的具体方法. 通用串行总线 ...

  3. linux配置usb主从_基于Linux的USB主/从设备之间的三种通信方式

    随着简单易用的USB接口日益流行,在嵌入式系统中添加对USB接口的支持已成为大势所趋.本文通过介绍Linux中支持USB的各种模块和库,分析了在Linux上利用USB实现高速串口和以太网连接等通信方式 ...

  4. Windows驱动开发学习笔记(三)—— 内核空间内核模块

    Windows驱动开发学习笔记(三)-- 内核空间&内核模块 内核空间 实验 第一步:编译如下代码 第二步:将 .sys 文件拷贝到虚拟机中 第三步:部署 .sys 文件并运行 第四步:创建一 ...

  5. 总结windows下堆溢出的三种利用方式

    创建时间:2004-04-08 文章属性:转载 文章提交:watercloud (watercloud_at_xfocus.org) 原文由Leven发在网络编程版: https://www.xfoc ...

  6. 常见可控硅带驱动 光耦 三极管 电阻三种方式

    常见可控硅带驱动 光耦 三极管 电阻三种方式,注意共的线哦

  7. 机器人驱动方式有哪三种?

    机器人驱动方式有哪三种? 驱动系统 焊接机器人能够有计划地运动的原因与其自身完整的运动机构以及相应的控制系统和驱动系统密不可分.焊接机器人的动力来自电力,液压或气压.当前,市场上的机器人主要使用三种驱 ...

  8. 网络上连接的计算机必须要安装,[单选] Windows XP中提供了三种组件,实现不同的网络功能。如果计算机需要连接到Internet,必须安装()。...

    [单选] Windows XP中提供了三种组件,实现不同的网络功能.如果计算机需要连接到Internet,必须安装(). 更多相关问题 [单选] 乙炔与空气或氧气混合达到自燃温度在()下也能爆炸 [单 ...

  9. SCPI基本语法和三种通信方式

    1.SCPI基本语法介绍 基本语法包括: 1.语法结构: 2.语法实例/例化. 2.仪器的三种通信方式 ethernet.usb device .GPIB 其中USB host可以通过选件转接到GPI ...

最新文章

  1. ad域不去用frs_年轻人就是不喜欢喝茶?那就用高级时尚的茶包装去吸引
  2. es任务 如何kill_kill进程的方法
  3. Android笔记:Fragment与Activity之间的交互,onAttach,退出最后一个 fragment
  4. IE从服务器上获取json后转为下载提示的bug
  5. C语言:输入两个数,输出最大公约数,最小公倍数
  6. 理解矩阵,矩阵背后的现实意义 [转]
  7. C++递归或非递归实现n的阶乘
  8. Wireshark初步入门
  9. 说说微信聊天记录收费这件事
  10. 从零开始构建MSBuild C#项目文件
  11. 东鹏特饮占据市场第二的背后:数据让我们比谁都了解消费者!
  12. 组态王、力控等传统组态软件通过互联网远程监控PLC
  13. cocos creator制作微信抖音小游戏《黄金矿工》
  14. 截止频率计算公式wc_计算截止频率Wc的快速方法
  15. win10清理_别人都说win10不需要装电脑管家,那电脑产生的垃圾该怎么清理呢
  16. 处理Elasticsearch集群yellow和red状态
  17. 2014 SuperMap GIS自主创新与应用研讨会资料集
  18. 微信公众号推文内可以添加附件了吗?
  19. 不使用端口映射,让自己的内网服务器的某个端口映射到外网
  20. 在马来西亚做it总监_马来西亚的IT行业急剧增长 - 但需要做的更多

热门文章

  1. isa 2006 下发布 owa
  2. javascript 值传递与作用域
  3. Linux培训之系统升级
  4. Merge Intervals
  5. 周4早上搜索引擎分析 - crmim.com| MSCRM开发者之家
  6. 电脑装windows和ubuntu,如何卸载ubuntu系统
  7. Python语言的有限状态机实现样例
  8. 微软正式开源Blazor ,将.NET带回到浏览器
  9. python中的reduce、lambda函数
  10. oracle 存储过程定义及调试,并终于被C# 调用 代码