就像题目上说的,今天在写一个例子代码时遇到了这个问题,下面是当时驱动层和应用层的代码:

#include <ntddk.h>
#define BASE_CODE 0x800
#define CREATE_THREAD_COMMAND CTL_CODE(FILE_DEVICE_UNKNOWN, BASE_CODE + 1, METHOD_BUFFERED, FILE_ANY_ACCESS)
#define DEVICE_NAME L"\\Device\\ThreadDevice"
#define LINK_NAME L"\\??\\ControlDevice"
VOID DriverUnload(PDRIVER_OBJECT pDriverObject)
{
UNICODE_STRING uLinkName;
PDEVICE_OBJECT pDev = pDriverObject->DeviceObject;
RtlInitUnicodeString(&uLinkName, LINK_NAME);
IoDeleteSymbolicLink(&uLinkName); IoDeleteDevice(pDev);
DbgPrint("Goodbye world!\n");
} VOID MyProcessThread(PVOID pContext)
{
//获取当前发送IRP请求的线程名
PEPROCESS pCurrProcess = IoGetCurrentProcess();
PTSTR pProcessName = (PTSTR)((CHAR*)pCurrProcess + 0x174);
// UNREFERENCED_PARAMETER(pContext);
DbgPrint("MyProcessThread Current Process %s\n", pProcessName);
PsTerminateSystemThread(0);
} VOID SystemThread(PVOID pContext)
{
//获取系统进程名
PEPROCESS pCurrProcess = IoGetCurrentProcess();
PTSTR pProcessName = (PTSTR)((CHAR*)pCurrProcess + 0x174);
// UNREFERENCED_PARAMETER(pContext);
DbgPrint("MyProcessThread Current Process %s\n", pProcessName);
PsTerminateSystemThread(0);
} VOID CreateThread_Test()
{
HANDLE hSysThread = NULL;
HANDLE hMyProcThread = NULL;
NTSTATUS status;
//创建系统进程
status = PsCreateSystemThread(&hSysThread, 0, NULL, NULL, NULL, SystemThread, NULL);
//创建用户进程
status = PsCreateSystemThread(&hMyProcThread, 0, NULL, NtCurrentProcess(), NULL, MyProcessThread, NULL);
} NTSTATUS IoControlDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
NTSTATUS status = STATUS_SUCCESS;
PIO_STACK_LOCATION pIrps = IoGetCurrentIrpStackLocation(Irp);
if(CREATE_THREAD_COMMAND == pIrps->Parameters.DeviceIoControl.IoControlCode)
{
//创建线程
CreateThread_Test();
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
NTSTATUS DriverEntry(
IN PDRIVER_OBJECT pDriverObject,
IN PUNICODE_STRING pRegistryPath)
{
UNICODE_STRING uDeviceName;
UNICODE_STRING uSymbolicName;
PDEVICE_OBJECT pDevObj;
NTSTATUS status;
DbgPrint("Hello, world\n");
pDriverObject->DriverUnload = DriverUnload; //初始化设备名称和链接名称
RtlInitUnicodeString(&uDeviceName, DEVICE_NAME);
RtlInitUnicodeString(&uSymbolicName, LINK_NAME);
//创建设备对像
status = IoCreateDevice(pDriverObject, 0, &uDeviceName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDevObj);
if(!NT_SUCCESS(status))
{
DbgPrint("Create Device Error!\n");
return status;
} status = IoCreateSymbolicLink(&uSymbolicName, &uDeviceName);
if(!NT_SUCCESS(status))
{
DbgPrint("Create SymbolicLink Error!\n");
IoDeleteDevice(pDevObj);
return status;
}
//设置IRP_MJ_DEVICE_CONTROL分发派遣函数
pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = IoControlDispatch;
return STATUS_SUCCESS;
}
#include <stdio.h>
#include <Windows.h>
#include <tchar.h>
#define BASE_CODE 0x800
#define CREATE_THREAD_COMMAND CTL_CODE(FILE_DEVICE_UNKNOWN, BASE_CODE + 1, METHOD_BUFFERED, FILE_ANY_ACCESS) int _tmain(int argc, TCHAR *argv[])
{
BOOL bRet = FALSE;
HANDLE hDevice = CreateFile(_T("\\\\.\\ControlDevice"), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (INVALID_HANDLE_VALUE == hDevice)
{
printf("Create File Error %d \n", GetLastError());
_tsystem(_T("PAUSE"));
return 0;
} printf("Create File Success hDevice = %08x\n", hDevice);
bRet = DeviceIoControl(hDevice, CREATE_THREAD_COMMAND, NULL, 0, NULL, 0, NULL, NULL);
if (!bRet)
{
printf("Control Device Error, code : %d\n", GetLastError());
}
_tsystem(_T("PAUSE"));
return 0;
}

这些代码非常简单,就是直接在应用层通过CreateFile打开,然后下发一个控制命令,驱动层接收到这个命令,创建两个线程,一个获取当前下发命令的应用程序的进程名,一个获取系统进程的进程名。

这段代码当时主要是有两个问题,第一个就是CreateFile打开时错误,并返回错误码1,乍看好像没有什么问题,其实这个问题我估计还是自己对应用层如何调用驱动层不太熟。首先驱动程序跟应用层的窗口程序类似,都是靠事件驱动的,在窗口程序中把这种事件叫做消息,窗口程序的主要功能是处理各种消息,由系统根据消息负责调用消息处理函数,驱动同样是这样。驱动中的设备对象就好像窗口一样,应用层下发的事件都是针对设备对象的。应用层针对不同设备对象下发的请求通过I/O管理器进行封装,变为一个个的IRP,根据不同的设备对象所属的驱动程序的不同,系统会自动调用我们事先准备好的处理程序,在程序中主要做这样几件事:

1. 对下发的事件进行适当的处理

2. 决定下发这个IRP或者结束这个IRP

3. 决定如何向I/O管理器和本层驱动程序返回值

I/O管理器会根据返回的值来决定如何给上层返回一个值,就拿CreateFile来说,这个API在调用时会经过I/O管理器生成一个IRP_MJ_CREATE类型的IRP,系统根据函数所针对的设备(这个设备可以通过第一个参数知道)找到对应的驱动,然后调用驱动中对应的处理函数,然后将这个处理函数中返回的值返回给I/O管理器,I/O管理器根据这个值决定如何返回值给应用层的API。说道这,这个问题的答案基本上已经出来了,这个问题的原因就是这段代码没有给定IRP_MJ_CREATE的处理函数,I/O管理器并没有收到一个成功的返回,所以它给应用层返回一个错误,我们加上一个Create的处理函数,并在DriverEntry中指定就好了。

NTSTATUS CreateDispatch(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
{
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
} //然后需要在DriverEntry中加上这句话,相当于在系统中注册了一个Create事件的处理函数
pDriverObject->MajorFunction[IRP_MJ_CREATE] = CreateDispatch;

这里有两个NTSTATUS的值,通过return返回的是给驱动程序的,而通过Irp->IoStatus.Status返回的是给I/O管理器的,上面说的I/O管理器没有收到成功,说的也是这个值没有给STATUS_SUCCESS

做完这些工作,这个问题就这样解决了,但是接着执行后面的代码,发现程序崩溃了,会弹出一个内存读写错误的提示框,这个时候可以肯定是应用层的问题,因为如果是内核层出现内存读写错误,系统肯定蓝屏了。当时我推测可能是句柄为NULL,或者DeviceIoControl中哪个缓冲区不能为NULL,为了知道是哪的问题,我在调用DeviceIoControl之前加了一条输出语句,我发现这条语句输出的句柄值是正常的,那就肯定是DeviceIoControl的问题,我先试着吧所有的输入输出缓冲区都给定了一个值,通过排查最后发现是倒数第二个参数不能为NULL,这个参数表示的是驱动层实际返回的缓冲区的大小。最后通过查看驱动层的代码,我终于知道这个值为什么不能为NULL。这就要说到DeviceIoControl与驱动通信的方式,DeviceIoControl的定义如下:

BOOL DeviceIoControl(
HANDLE hDevice,
DWORD dwIoControlCode,
LPVOID lpInBuffer,
DWORD nInBufferSize,
LPVOID lpOutBuffer,
DWORD nOutBufferSize,
LPDWORD lpBytesReturned,
LPOVERLAPPED lpOverlapped
);

DeviceIoControl通过dwIoControlCode向驱动下发控制码,这个控制码在驱动中可以通过IO_STACK_LOCATION 结构中的Parameters.DeviceIoControl.IoControlCode值获得。另外函数根据lpInBuffer和nInBufferSize来给驱动传递数据,通过参数lpOutBuffer和nOutBufferSize来接受来自驱动上传的数据,另外还有一个是驱动真实上传数据的大小,大小都是以字节为单位的。那么这个真实大小是怎么来的呢?答案就是通过Irp->IoStatus.Information这个值,I/O管理器取这个值,将它填充到lpBytesReturned所指向的内存中,既然我们在驱动中指定了这个值为0,自然要给它在应用层分配相应的缓冲区了,前面的由于给的是NULL,I/O管理器不可能将这个值填入NULL缓冲区,所以自然会弹出这个内存读写的错误。

最后来总结下:

1. 如果我们要打开对应的驱动中的设备对象,在驱动层需要提供IRP_MJ_CREATE的处理函数,将返回给I/O管理器的值填入到IRP的IoStatus这个结构中。类似的,如果我们通过类似WriteFile、ReadFile的函数发送的相应的IRP,即使我们不需要对这个IRP进行特殊的处理,我们也需要为这些操作提供一个默认的处理函数,至少要向I/0管理器返回一个成功,这样应用层的函数才能调用成功。

2. DeviceIoControl函数,如果不需要跟驱动层进行交互,那么他的输入输出缓冲区是可以给NULL的,但是由于I/O管理器会像它返回驱动层实际返回的数据的大小,所以这个真实大小的缓冲区一定不能为NULL

自己写的驱动用CreateFile打开时错误码返回1的问题的更多相关文章

  1. MYSQL连接时错误码2059解决办法

     一.问题原因: 如上图的报错提示可知,报错原因是caching_sha2_password不能加载. 这是因为8.0之后mysql更改了密码的加密规则,而目前已有的客户端连接软件还不支持Mysql8 ...

  2. JavaWeb项目中获取对Oracle操作时抛出的异常错误码

    最近在项目中碰到了这么一个需求,一个JavaWeb项目,数据库用的是Oracle.业务上有一个对一张表的操作功能,当时设置了两个字段联合的唯一约束.由于前断没有对重复字段的校验,需要在插入时如果碰到唯 ...

  3. DirectUI中模态对话框和菜单的原理(自己控制整个Windows消息循环。或者,用菜单模拟窗体打开时用SetCapture取得控制权,一旦窗体收到WM_CAPTURECHANGED消息就把窗体退出)

    经常有人问关于模态对话框和系统菜单内部实现原理方面的问题, 因为系统通过API隐藏了太多细节,这2个问题确实令初学者甚至是有经验的开发者困扰, 下面是我个人的一些经验总结. 先说模态对话框,外部看模态 ...

  4. 使EditText不要在页面打开时自动获取焦点(因为软键盘会自动弹出)

    当页面上有EditText时,第一个EditText会在页面打开时自动的获取焦点,这样带来的问题就是系统键盘会自动的弹出来. 解决方法比较简单只要为EditText的父控件设置一下就行了: andro ...

  5. cmder设置打开时的默认目录

    cmder设置打开时的默认目录 打开cmder自动进入工作目录,怎么配置? http://superuser.com/questions/1005285/run-a-bat-file-with-cmd ...

  6. Visual Studio2012打开时弹出“遇到异常:这可能是由某个扩展导致的”错误的解决办法

    Visual Studio2012打开时弹出"遇到异常:这可能是由某个扩展导致的"错误的解决办法: 具体问题如下: 分析原因:网上搜集了以下,出现异常的原因是安装了第三方控件,然后 ...

  7. vs2013安装闪退及vs2010 vs2013打开时提示 未能完成的操作 及vs2013安装时出现图片后闪退

    vs2013打开时提示如上图,vs2010只有  未能完成的操作  这样的提示. 这时.net 4.0开发的程序打开也毫无反应,应该是.net framework出了问题.查看控制面板-卸载程序,发现 ...

  8. 今天在Mac机器上使用了Flex Builder编辑了一个源代码文件,保存后使用vim命令去打开时发现系统自动在每一行的结尾添加了^M符号,其实^M在Linux/Unix中是非常常见的,也就是我们在Win中见过的/r回车符号。由于编辑软件的编码问题,某些IDE的编辑器在编辑完文件之后会自动加上这个^M符号。看起来对我们的源代码没有任何影响,其实并不然,当我们把源代码文件Check In到svn之类

    今天在Mac机器上使用了Flex Builder编辑了一个源代码文件,保存后使用vim命令去打开时发现系统自动在每一行的结尾添加了^M符号,其实^M在Linux/Unix中是非常常见的,也就是我们在W ...

  9. stm32学习笔记----双串口同时打开时的printf()问题

    stm32学习笔记----双串口同时打开时的printf()问题 最近因为要使用串口2外接PN532芯片实现通信,另一方面,要使用串口1来将一些提示信息输出到上位机,于是重定义了printf(),使其 ...

随机推荐

  1. Explicit Semantic Analysis (ESA)

    有别于LSA (Latent Semantic Analysis), 下列文章提出一种ESA (Explicit Semantic Analysis), 并介绍怎样使用ESA来进行语义相关性和文本分类 ...

  2. 翻译:Identifier Qualifiers标识限定符

    */ .hljs { display: block; overflow-x: auto; padding: 0.5em; color: #333; background: #f8f8f8; } .hl ...

  3. Node之Express服务器启动安装与配置

    首先安装express-generator cnpm i -g express-generator 使用express --version查看express版本 生成express服务 express ...

  4. SQL Server 修改AlwaysOn共享网络位置

    标签:MSSQL/故障转移 概述 很多人一开始搭建Alwayson的时候对于共享网络位置的选择不是很重视, 导致后面需要去修改这个路径.但是怎样修改这个路径呢?貌似没有给出具体的修改选项,但是还是有地 ...

  5. Android Looper原理分析

    实际业务使用场景: 某业务场景需要将本地数据传递到服务端,服务端再返回传递成功或者失败的信息. 1. 失败时: 重传5次 2.设置客户端请求的最小时间间隔,这个间隔内最多请求1次 具体逻辑如下:(这里 ...

  6. Spring+MVC+Mybatis整合

    本文是对慕课网上"搞定SSM开发"路径的系列课程的总结,详细的项目文档和课程总结放在github上了.点击查看 什么是秒杀业务 网站售卖某产品时,规定在某个日期开始售卖限量的产品, ...

  7. bzoj 1835: [ZJOI2010]base 基站选址

    Description 有N个村庄坐落在一条直线上,第i(i>1)个村庄距离第1个村庄的距离为Di.需要在这些村庄中建立不超过K个通讯基站,在第i个村庄建立基站的费用为Ci.如果在距离第i个村庄 ...

  8. bzoj 4515: [Sdoi2016]游戏

    Description Alice 和 Bob 在玩一个游戏. 游戏在一棵有 n 个点的树上进行.最初,每个点上都只有一个数字,那个数字是 123456789123456789. 有时,Alice 会 ...

  9. bzoj 1996: [Hnoi2010]chorus 合唱队

    Description Input Output Sample Input 4 1701 1702 1703 1704 Sample Output 8 HINT Source 因为只会在区间的两端进行 ...

  10. 对于group by 和 order by 并用 的分析

    今天朋友问我一个sql查询. 需求是 找到idapi最近那条数据,说明idapi 是重复的,于是就简单的写了 SELECT * FROM `ag_alarm_history`   group by ` ...