驱动开发(8)处理设备I/O控制函数DeviceIoControl
在上面的两篇博文中,介绍了IRP与派遣函数,以及我们通过了一个样例“磁盘设备的绝对读写”来演示了在应用程序中是怎样向一个设备发出I/O请求的。这篇博文将演示在驱动程序中处理一个很easy的I/O请求——由DeviceIoControl这个Win32API经过一系列的调用,在内核中由I/O管理器构造生成的IRP_MJ_DEVICE_CONTROL这个IRP。
我们先来看看DeviceIoControl这个函数的原型,此函数向某个打开的设备所在驱动程序的派遣函数中发送IRP:IRP_MJ_DEVICE_CONTROL。函数原型:
BOOL WINAPI DeviceIoControl(
_In_ HANDLE hDevice,
_In_ DWORD dwIoControlCode,
_In_opt_ LPVOID lpInBuffer,
_In_ DWORD nInBufferSize,
_Out_opt_ LPVOID lpOutBuffer,
_In_ DWORD nOutBufferSize,
_Out_opt_ LPDWORD lpBytesReturned,
_Inout_opt_ LPOVERLAPPED lpOverlapped
);
hDevice:操作是要运行的设备句柄。
使用 CreateFile 函数打开。
dwIoControlCode:操作的控制代码。
须要注意的是,这个控制码不是随便定的,为了方便定义控制码,微软提供了一个CTL_CODE宏。控制码的结构例如以下:
本例中博主定义了这种一个ioctl控制码:
#define IOCTL1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS)
来看看这个宏的用法:
首先是这个宏的定义:
#define CTL_CODE(DeviceType, Function, Method, Access) (
((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method))DeviceType:设备类型。IoCreateDevice使用的设备类型,详细參加我之前的博文”NT驱动的基本结构“
Function:定义设备类别中的一个操作。0-2047和4096以后被微软保留,2048-4095(0x800-0xFFF)留给我们使用。
Method:定义操作模式
METHOD_BUFFERED:缓冲区方法。本例使用这样的方法
METHOD_IN_DIRECT:直接输入
METHOD_OUT_DIRECT:直接输出
METHOD_NEITHER:两者都不,即其它方法对于 Windows 嵌入式设备,此字段将被忽略。始终使用 METHOD_BUFFERED。
Access:一般用FILE_ANY_ACCESS,全部权限。
lpInBuffer:(可选)指向输入缓冲区的指针。
nInBufferSize:输入缓冲区以字节为单位的大小。
lpOutBuffer:(可选)指向输出缓冲区的指针,
nOutBufferSize:输出缓冲区以字节为单位的大小。
lpBytesReturned:(可选)指向接收“输出缓冲区中接收的数据的大小”的变量的指针。假设输出缓冲区太小,无法接收不论什么数据,则GetLastError返回ERROR_INSUFFICIENT_BUFFER。此时lpBytesReturned是零。假设输出缓冲区太小,不能容纳全部数据,但能够容纳一些条目,一些驱动可能将尽可能多的返回数据。在这样的情况下,GetLastError返回ERROR_MORE_DATA,然后lpBytesReturned指示接收的数据量。应用程序能够指定一个新的起点再次调用DeviceIoControl。假设lpOverlapped是NULL,那么lpBytesReturned不能为
NULL。
lpOverlapped:(可选)OVERLAPPED结构的指针。假设打开hDevice时没有指定FILE_FLAG_OVERLAPPED标志,lpOverlapped将被忽略。假设打开 hDevice 时指定了FILE_FLAG_OVERLAPPED 标志,则作为异步操作运行。
在这样的情况下,lpOverlapped必须指向有效的重叠结构,而且必须包括事件对象的句柄。
否则。该函数会失败。
注:异步操作,为 DeviceIoControl 马上返回,而且当在操作完毕时终止的事件对象的操作。
返回值:假设该操作成功完毕,则返回值不为零。
假设操作失败。或处于挂起状态,则返回值为零。若要获取扩展的错误信息,请调用GetLastError
注意:IRP_MJ_DEVICE_CONTROL这个IRP在Win32子系统中调用DeviceIoControl生成。在NT Native层或内核模式下应该使用ZwDeviceIoControlFile。
事实上这个IRP能够用于应用程序与驱动程序通信。
先上应用程序的源代码,还是打开我们设备的符号连接。并使用DeviceIoControl函数向驱动程序发送一个控制码,我想我不用再解释什么了,假设你不能理解,请回过头看看我的前两篇和更早的博文。
源代码:
#include "stdafx.h"
#include<Windows.h> #define IOCTL1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) int _tmain(int argc, _TCHAR* argv[])
{
HANDLE handle = CreateFileA("\\\\.\\MyDevice1_link", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (handle == INVALID_HANDLE_VALUE){
MessageBoxA(0, "打开设备失败", "错误", 0);
return 0;
}
unsigned char buffer[50] = { 0 };
unsigned char buffer2[50] = { 0 };
DWORD len;
sprintf((char*)buffer, "hello, driver\r\n");
if (DeviceIoControl(handle, IOCTL1, buffer, strlen((char*)buffer), buffer2, 49, &len, NULL)){
printf("len: %d\n", len);
for (int i = 0; i < len; i++){
printf("0x%02X ",buffer2[i]);
}
}
getchar();
CloseHandle(handle);
return 0;
}
我们再来看看驱动程序的源代码,在派遣函数中处理这个IRP,我们获取了应用程序发送来的控制码后输出输入缓冲区的数据,并将输出缓冲区填充为0xF1:
#include <ntddk.h>
extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject);
extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp);
extern "C" NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp); #define IOCTL1 CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) typedef struct _DEVICE_EXTENSION {
UNICODE_STRING SymLinkName; //我们定义的设备扩展里仅仅有一个符号链接名成员
} DEVICE_EXTENSION, *PDEVICE_EXTENSION; extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistryPath)
{
DbgPrint("DriverEntry\r\n"); pDriverObject->DriverUnload = DriverUnload;//注冊驱动卸载函数 //注冊派遣函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = DefDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_CLOSE] = DefDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_WRITE] = DefDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_READ] = DefDispatchRoutine;
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoctlDispatchRoutine; NTSTATUS status;
PDEVICE_OBJECT pDevObj;
PDEVICE_EXTENSION pDevExt; //创建设备名称的字符串
UNICODE_STRING devName;
RtlInitUnicodeString(&devName, L"\\Device\\MyDevice1"); //创建设备
status = IoCreateDevice(pDriverObject, sizeof(DEVICE_EXTENSION), &devName, FILE_DEVICE_UNKNOWN, 0, TRUE, &pDevObj);
if (!NT_SUCCESS(status))
return status; pDevObj->Flags |= DO_BUFFERED_IO;//将设备设置为缓冲I/O设备,关于缓冲I/O设备将会在下一篇博文中讲。
pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展 //创建符号链接
UNICODE_STRING symLinkName;
RtlInitUnicodeString(&symLinkName, L"\\?? \\MyDevice1_link");
pDevExt->SymLinkName = symLinkName;
status = IoCreateSymbolicLink(&symLinkName, &devName);
if (!NT_SUCCESS(status))
{
IoDeleteDevice(pDevObj);
return status;
}
return STATUS_SUCCESS;
} extern "C" VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
DbgPrint("DriverUnload\r\n");
PDEVICE_OBJECT pDevObj;
pDevObj = pDriverObject->DeviceObject; PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;//得到设备扩展 //删除符号链接
UNICODE_STRING pLinkName = pDevExt->SymLinkName;
IoDeleteSymbolicLink(&pLinkName); //删除设备
IoDeleteDevice(pDevObj);
} extern "C" NTSTATUS DefDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Enter DefDispatchRoutine\r\n");
NTSTATUS status = STATUS_SUCCESS;
pIrp->IoStatus.Status = status;
pIrp->IoStatus.Information = 0;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
}
extern "C" NTSTATUS IoctlDispatchRoutine(PDEVICE_OBJECT pDevObj, PIRP pIrp)
{
DbgPrint("Enter IoctlDispatchRoutine\r\n");
NTSTATUS status = STATUS_SUCCESS; //得到I/O堆栈的当前这一层,也就是IO_STACK_LOCATION结构的指针
PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(pIrp); ULONG in_size = stack->Parameters.DeviceIoControl.InputBufferLength;//得到输入缓冲区的大小
ULONG out_size = stack->Parameters.DeviceIoControl.OutputBufferLength;//得到输出缓冲区的大小
ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;//得到控制码 PVOID buffer = pIrp->AssociatedIrp.SystemBuffer;//得到缓冲区指针 switch (code)
{ // process request
case IOCTL1:
DbgPrint("====Get ioctl code 1\r\n");
//显示输入缓冲区数据
DbgPrint((PCSTR)buffer); //将输出缓冲区填充字符
RtlFillMemory(buffer, out_size, 0xF1);
break;
default:
status = STATUS_INVALID_VARIANT;
//假设是没有处理的IRP。则返回STATUS_INVALID_VARIANT。这意味着用户模式的I/O函数失败,但并不会设置GetLastError
} // 完毕IRP
pIrp->IoStatus.Status = status;//设置IRP完毕状态,会设置用户模式下的GetLastError
pIrp->IoStatus.Information = out_size;//设置操作的字节
IoCompleteRequest(pIrp, IO_NO_INCREMENT);//完毕IRP,不添加优先级
return status;
}
效果图:
这一篇中,本来打算详解一下处理IRP的过程。可是在这个IRP上。实在是不好讲。所下面一篇(处理缓冲I/O设备的读写请求)中介绍IRP处理的具体过程。含义。读和写都是最主要的I/O请求之中的一个。并且也是最好理解的,所下面一篇中,具体介绍怎样处理IRP以及操作系统对于缓冲I/O设备(同步模式下)读写请求的API(ReadFile(Ex)WriteFile(Ex))进入内核后的具体实现过程。
驱动开发(8)处理设备I/O控制函数DeviceIoControl的更多相关文章
- 驱动开发入门——NTModel
上一篇博文中主要说明了驱动开发中基本的数据类型,认识这些数据类型算是驱动开发中的入门吧,这次主要说明驱动开发中最基本的模型--NTModel.介绍这个模型首先要了解R3层是如何通过应用层API进入到内 ...
- Linux内核(17) - 高效学习Linux驱动开发
这本<Linux内核修炼之道>已经开卖(网上的链接为: 卓越.当当.china-pub ),虽然是严肃文学,但为了保证流畅性,大部分文字我还都是斟词灼句,反复的念几遍才写上去的,尽量考虑到 ...
- linux驱动开发流程
嵌入式linux驱动开发流程嵌入式系统中,操作系统是通过各种驱动程序来驾驭硬件设备的.设备驱动程序是操作系统内核和硬件设备之间的接口,它为应用程序屏蔽了硬件的细节,这样在应用程序看来,硬件设备只是一个 ...
- 《Windows内核安全与驱动开发》 5.1&5.2 内核与应用方面的编程
<Windows内核安全与驱动开发>阅读笔记 -- 索引目录 <Windows内核安全与驱动开发> 5.1&5.2 内核与应用方面的编程 一.生成控制设备 如果一个驱 ...
- C++第三十三篇 -- 研究一下Windows驱动开发(一)内部构造介绍
因为工作原因,需要做一些与网卡有关的测试,其中涉及到了驱动这一块的知识,虽然程序可以运行,但是不搞清楚,心里总是不安,觉得没理解清楚.因此想看一下驱动开发.查了很多资料,看到有人推荐Windows驱动 ...
- Windows7下驱动开发与调试体系构建——2.R3与R0的通信示例
目录/参考资料:https://www.cnblogs.com/railgunRG/p/14412321.html 在阅读本节前,建议先阅读<Windows内核安全与驱动开发>第五章内容, ...
- 使用IdleTest进行TDD单元测试驱动开发演练(3) 之 ASP.NET MVC
一.[前言] (1)本文将用到IOC框架Unity,可参照<Unity V3 初步使用 —— 为我的.NET项目从简单三层架构转到IOC做准备>(2)本文的解决方案是基于前述<使用I ...
- 使用IdleTest进行TDD单元测试驱动开发演练(2)
[前言] 1. 有关上篇请参见<使用IdleTest进行TDD单元测试驱动开发演练(1)>,有关本篇用到Entity Framework Code First请参见<使用NuGet助 ...
- 使用IdleTest进行TDD单元测试驱动开发演练(1)
[前言] 开发工具:Visual Studio 2012 测试库:Visual Studio 2012自带的MSTest DI框架:Unity 数据持久层:Entity Framework 前端UI: ...
随机推荐
- 关于17个Cr的430采购的注意事项 430F
430F不锈钢是在430钢上加上易切削性能的钢种.用于自动车床.螺栓和螺母等.430LX在430钢中添加Ti或Nb.降低C含量,改善了加工性能的和焊接性能.用于热水罐.供热水系统.卫生器具.家庭用耐用 ...
- 2B课程笔记分享_StudyJams_2017
课程2B-创建交互式应用(下) 概述 课程2B的内容主要包括:使用变量来更新欲显示在屏幕上的内容,为按钮添加事件响应(联系XML属性与Java方法)逻辑等. 后续的课程会逐步深入地讲解使用Java开发 ...
- page事件
using System; using System.Data; using System.Configuration; using System.Web; using System.Web.Secu ...
- 【Linux】VMware安装VMware Tools工具
VMware Tools是VMware虚拟机中自带的一种增强工具,相当于VirtualBox中的增强功能(Sun VirtualBox Guest Additions),是VMware提供的增强虚拟显 ...
- 【JSP】简单登陆界面
学生登陆查询系统 1 程序的主要功能及特点 实现一个登录界面的基本功能,具体要求: 登录界面login.jsp含有表单,用户能够输入用户名和密码,并提交表单给verify.jsp. Verify.js ...
- (转)Openlayers 2.X加载高德地图
http://blog.csdn.net/gisshixisheng/article/details/44853881 概述: 前面的有篇文章介绍了Openlayers 2.X下加载天地图,本节介绍O ...
- Typeclassopedia
https://wiki.haskell.org/wikiupload/8/85/TMR-Issue13.pdf By Brent Yorgey, byorgey@gmail.com Original ...
- 【深度学习框架】使用PyTorch进行数据处理
在深度学习中,数据的处理对于神经网络的训练来说十分重要,良好的数据(包括图像.文本.语音等)处理不仅可以加速模型的训练,同时也直接关系到模型的效果.本文以处理图像数据为例,记录一些使用PyTorc ...
- [luogu1073 Noip2009] 最优贸易 (dp || SPFA+分层图)
传送门 Description C 国有n 个大城市和m 条道路,每条道路连接这n 个城市中的某两个城市.任意两个 城市之间最多只有一条道路直接相连.这m 条道路中有一部分为单向通行的道路,一部分 为 ...
- [CodeForces1059E] Split the Tree
树形DP. 用倍增处理出来每个点往上能延伸出去的最远路径,nlogn 对于每个节点,如果它能被后代使用过的点覆盖,就直接覆盖,这个点就不使用,否则就ans++,让传的Max改成dp[x] #inclu ...