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

#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. java.lang.IllegalStateException: attempt to re-open an already-closed object

    attempt to re-open an already-closed object 字面理解,试图再次打开已经关闭的对象.这是我在操作sqlited的时候出现的错误, 我在一个activity里面 ...

  2. Android Root原理初探

    Root Linux:Root == Windows:Adminstrator Android是Linux系统吗? 操作系统 = 系统内核 + 文件系统 Linux发行版:Linux内核 + 文件系统 ...

  3. 【CODEFORCES】 A. Dreamoon and Sums

    A. Dreamoon and Sums time limit per test 1.5 seconds memory limit per test 256 megabytes input stand ...

  4. Elasticsearch批处理操作——bulk API

    Elasticsearch提供的批量处理功能,是通过使用_bulk API实现的.这个功能之所以重要,在于它提供了非常高效的机制来尽可能快的完成多个操作,与此同时使用尽可能少的网络往返. 1.批量索引 ...

  5. Intellij IDEA编译代码出现红色标志

    如图 原因:项目没有模块化加载 解决方法: 快捷键 Ctrl+Alt+Shift+S 进入 Project Structure 界面,选择 Modules --> Sources -->选 ...

  6. jar包后台启动--nohup篇

    直接java -jar TestHttps-0.0.1-SNAPSHOT.jar的话是前段启动,但是窗口关闭之类的程序也就关闭了 我们可以nohup java -jar TestHttps-0.0.1 ...

  7. android 网络请求Volley的简单使用

    下载到本地jar包或者在线导入,jar地址:链接:http://pan.baidu.com/s/1gf3VZAb 密码:mmye //定义变量 private RequestQueue mQueue= ...

  8. 54、js初识

    今天这篇将介绍javascript,学完javascript就可以使你的网页动起来. 一.JavaScript概述  1.JavaScript的历史 1992年Nombas开发出C-minus-min ...

  9. 【java设计模式】【结构模式Structural Pattern】合成模式Composite Pattern

    package com.tn.pattern; import java.util.Vector; public class Client { public static void main(Strin ...

  10. JavaScript基础5——关于ECMAscript的函数

    ECMAScript的函数概述(一般定义到<head>标签之间) (1)定义函数,JavaScript一般有三种定义函数方法: *第一种是使用function语句定义函数(静态方法) fu ...