近一个多月以来一直在看驱动方面的东西
近一个多月以来一直在看驱动方面的东西,把马少华翻译的Programming the Microsoft Windows Drivers
Model的第一版算是过了一遍,但一直没有动手写一行代码。每个人的学习方式不同,对于我来说,一样东西如果没有一个框架上的大体把握,就象新到一个城市,一下还找不到方位感,说起一个地方,根本不知道处于自己所在位置的哪个方向,因而相当长的一段时间内会感觉到特别别扭。当然,也只是别扭而已,你知道这需要时间的,时间够了,有些东西会自然地沉积在你的身体里,然后可以不假思索地说出东西南北来,这么多年了,你多少对自己还保留着一点信心吧~呵呵。
老马翻译的是第一版,我机器上也有Walter Oney的第二版(E文),内容扩充了不少,我曾经试图直接看二版的,东西总要新一些吧。唉~看了一段时间,进度太慢了,E文的水准还处于边读边翻译的阶段,每句话都要过到脑子里先变成中文再理解,如果看自己已经熟悉的东西,可能还能好点,对于驱动新手来说,很多概念是新的,再这么折腾绕一回,老费劲了。想一想,还是别为难自己了,先对整体框架有了一个大体的把握,回头再看E文的也行吧。也不记得在哪找到了老马的中文版(好像还不是驱动网),下载下来才发现机器上老早就有了,嘿嘿,估计也静静地了存了两年了,这二年我都做了些什么呢,我问自己,在夜深人静的夜里。
看老马的中文就舒服多了,可以一目一行了吧,速度快了不少。应该说老马的文字还是比较讲究的,通顺流畅,这在技术高手中是很难得的。我总觉得一个高手能把某件事物明白无误地说清楚,这更反应了他对这件事物深刻和透彻的理解。当然不会是每件事情都可以和适合深入浅出生动活泼地来阐述的,这还得看说的是什么事情,也还得看你说给谁来听吧。
非常感谢老马所做的工作~
作为一个新手,常常有这样的困惑,一本
书
关于书的成语关于读书的排比句社区图书漂流公约怎么写关于读书的小报汉书pdf
,400页、500页的,在没有人指点的情况下,怎么读,我没有
办法
鲁班奖评选办法下载鲁班奖评选办法下载鲁班奖评选办法下载企业年金办法下载企业年金办法下载
,我完全不清楚哪些更重要,哪些是可以一笔带过的,哪些有难度的地方必须硬着头皮啃,哪些又是可以放一放的,哪些是在将来的应用中频繁使用的,而哪些又是以后几乎不会接触的。我只有从头开始,一章一节的往下读,不敢放过任何一个细节,怕影响到以后章节相关内容的理解,字斟句酌的,累死了,毫无阅读的快感可言,时间长了就开始烦,脑子里渐渐地成了一锅粥,概念混乱,开始迷迷瞪瞪。再赶上同学结婚了、比你还小一截的家伙生孩子了、老爹老妈来视察了、公司要你出差了、女朋友说要和你结婚,TNND~这东西撂在一边个把礼拜,实在是没有兴致再拿起来了,还是有不少阅读障碍和困难的,毕竟这不是艳情小说呀。
书应该越读越薄~这道理我知道,我也有这种体验。对于你理解和经过的东西,你会知道最重要的是什么,即便是一本可以做枕头用的厚书,你也会觉得没有什么太多内容。很多东西可以在需要使用的时候再去查找MSDN或者别的资料,只要你理解了最基本和最精要的核心。
其实在系统地读老马的译本之前,我也零零散散的看了些东西,网上查找了不少入门上手的东西,包括楚狂人的文件过滤驱动教程,这篇东西还真是个好东西,虽说不能算是入门级的教程,但我读起来还是颇有兴致,并且障碍不是很大,当然里面也有不少不甚明白的东西,但基本不影响对框架的理解。之后,开始读老马的译本,差不多花了一个月,算是囫囵过了一遍。其间还不敢上手写例程,准备读完以后,再细读一遍,把所有的有条件看到效果的例程录入一遍(需要开发板的就先放放)。唉~笨人就只有笨招,不过这
方法
快递客服问题件处理详细方法山木方法pdf计算方法pdf华与华方法下载八字理论方法下载
对于我来说效果还不错,以前核心编程那本就是这么来着,代码写一遍和读一遍还是不同的。
言归正题吧~
首先得开始搭建环境
操作系统为Windows XP SP2,接下来你得装个Visual Studio 6.0吧,外加上个SP5,当然你可以说VC6只是个编辑器,也不用非在VC的集成环境中编译驱动,是~没问题,但你总得干点别的活吧,公司里写不完的应用呀,我们同事在LINUX下的代码都在VC6中来写,然后再通过WinSCP传到LINUX下呢。接下来装什么呢,我又装了个VS.NET2003和MSDN .NET2003,这样SDK2003有了,呵呵,主要是MSDN的内容比较全,DDK的内容也有了,VS6的MSDN就没必要了。
接下来就是安装DDK了,网上找了个WINXP DDK,有134M,不记得在哪找到的了,好像还费了一番周折,WIN2000 DDK容易找到些。安装后,生成的开始菜单如下:
其实现在就可以开始干活了,但还不忙~
驱动程序得有个调试环境吧,驱动的调试总不能在VC6里呀,他们说得用WINDBG或者SOFTICE才能调试内核层面的代码,但SOFTICE在单机上就能调,而WINDBG还得准备两台机器,没那么多银子,那就SOFTICE了。上网找去~东转西转的,忍不住还是先看了下说姚明和李宇春有夫妻相的一个链接,找到了看雪学院(),哦,还有个Driver Studio3.2的玩意呢,早就听说有这么东西了,说是只要NEXT、NEXT,再NEXT就能生成驱动了,那就都给下了吧。
先解包Driver Studio压缩文件,安装,注意:这个Driver Studio的安装包还包括SOFTICE的安装,还是不要在Driver Studio里安装SOFTICE吧(有选项可以取消SOFTICE的安装),好像安装后的SOFTICE中,SYMBOL LOADER有问题,SYMBOL LOADER都有问题,那还怎么调试呢。
装完Driver Studio后,开始安装SOFTICE,应该很顺利的。如果你愿意,可以在Initialization string设置中添加“FAULTS off;SET MAXIMIZE on;SET FONT 3;CLS;X;”(注意没有引号),这样可使得SOFTICE窗口看起来舒服一点,当然你不加,也没有问题。
Driver Studio安装以后,会在VISUAL C,,的集成环境中添加工具条和菜单项。
关于Driver Studio的配置有不少内容,包括DDK的路径配置,目标操作系统等等,当然其中有些是DDK本身的配置,还有一点就是Driver Studio本身的LIB需要根据机器上安装的DDK重新编译一次才行。由
此也可以看出Driver Studio本身的LIB是对DDK的封装,类似于MFC对SDK的封装。 我自己尝试着用Driver Studio创建了一个最基本的驱动程序,打开一开,没头没尾的,就象最初看MFC的代码一样,没有了WinMain或main,有一些莫名的结构定义,整个代码没有了清晰的流程,一块一块的,似乎都不搭界,但其实在MFC内部将各个部分串接了起来,可是你最初怎么可能弄得清楚呢,如果不是侯杰的那本深入浅出MFC,光是靠查看MSDN的帮助,天知道怎么回事呀。即便是有那本难得的好书,又下了多少功夫才算明白了呢~我看还是算了,直接用DDK吧,至少你自己能把握的东西更多一些。
抛开Driver Studio的NEXT吧~
但是Driver Studio里的工具还是可以用的:
DriverMonitor和Symbolic Link Viewer等工具还是可以用的,虽然也有别的小工具(比如DevView或者WinObj)可以替代,在后面我会用到这两个工具软件。
关于环境的搭建就写这么多了,其中具体的安装可以上网查找详细的信息。
接下来该写一个驱动程序的”Hello World!”了,主要的目的是为了理顺产生一个驱动程序的通常模式,打通从编辑代码、编译连接、安装驱动、启动驱动、测试调用驱动、调试驱动代码、查看设备及驱动状态,最后到停止驱动整个过程的通道。
在这里我不打算
分析
定性数据统计分析pdf销售业绩分析模板建筑结构震害分析销售进度分析表京东商城竞争战略分析
具体代码,作为一个HELLOWORLD例程,应该说所有的代码都不难理解,本文的主要目的只是想记录我自己创建调试一个最简单的但是名副其实的驱动程序的完整过程。如果你现在觉得其中的代码还有困难,那就再回个头来看看IRP、IO_STACK_LOCATION、DriverEntry、MajorFunction、DEVICE_OBJECT、CreateFile、DeviceIoControl等等概念吧。
在这里我引用的代码是网上的一篇文章,就是介绍驱动版的“Hello World”的。(zhangjie0072的专栏,不知道现在还能否打开了,当时我是将网页保存在硬盘上了,这篇文章里介绍了不少概念,感谢zhangjie0072~)。
首先将代码录入(我先打印了网页,然后手工敲入所有代码,没有COPY和PASTE,开始阶段这种录入工作对于我来说是一个很好的熟悉过程,即便是重复的代码我也觉得很舒服,嘿嘿,当然,好在代码都不长)。
我还是使用VC6的集成环境来作为编辑器,当然你使用记事本也没问题。
四个文件:HelloWorld.c, HelloWorld.h, makefile, source.(都在D:\CurrentWork\DrvWork\HelloWorld目录下),
makefile和sources为编译的相关配置文件。
下面列出四个文件的完整代码(再次感谢zhangjie0072~),不过我都没有录入注释了,sorry。
=============================HelloWorld.h========================================
#ifndef __HELLOWORLD_H__
#define __HELLOWORLD_H__
#include
#define DEVICE_HELLO_INDEX 0x860
#define START_HELLOWORLD
CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define STOP_HELLOWORLD
CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_ACCESS)
#define NT_DEVICE_NAME L"\\Device\\HelloWorld"
#define DOS_DEVICE_NAME L"\\DosDevices\\HelloWorld"
NTSTATUS HelloWorldDispatch(IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp);
VOID HelloWorldUnload(IN PDRIVER_OBJECT DriverObject);
#endif
=================================HelloWorld.c=======================================
#ifndef __HELLOWORLD_C__
#define __HELLOWORLD_C__
#define DEBUGMSG
#include "HelloWorld.h"
NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath) {
NTSTATUS ntStatus=STATUS_SUCCESS;
PDEVICE_OBJECT IpDeviceObject=NULL;
UNICODE_STRING DeviceNameString;
UNICODE_STRING DeviceLinkString;
#ifdef DEBUGMSG
DbgPrint("hi, Starting DriverEntry()\n");
#endif
RtlInitUnicodeString(&DeviceNameString,NT_DEVICE_NAME);
ntStatus=IoCreateDevice(DriverObject,0,&DeviceNameString,FILE_DEVICE_UNKNOWN,0,FALSE,&Ip
DeviceObject);
if(!NT_SUCCESS(ntStatus))
{
#ifdef DEBUGMSG
DbgPrint("hi, Error IoCreateDevice()\n");
#endif
goto Error;
}
RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
ntStatus=IoCreateSymbolicLink(&DeviceLinkString,&DeviceNameString);
if(!NT_SUCCESS(ntStatus))
{
#ifdef DEBUGMSG
DbgPrint("hi, Error IoCreateSymbolicLink()\n");
#endif
goto Error;
}
DriverObject->MajorFunction[IRP_MJ_CREATE]=HelloWorldDispatch;
DriverObject->MajorFunction[IRP_MJ_CLOSE]=HelloWorldDispatch;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL]=HelloWorldDispatch;
DriverObject->DriverUnload=HelloWorldUnload;
return ntStatus;
Error:
#ifdef DEBUGMSG
DbgPrint("hi, Error DriverEntry()\n");
#endif
return ntStatus;
}
NTSTATUS HelloWorldDispatch(IN PDEVICE_OBJECT DeviceObject,IN PIRP pIrp)
{
NTSTATUS ntStatus=STATUS_SUCCESS;
ULONG IoControlCodes=0;
PIO_STACK_LOCATION IrpStack=NULL;
pIrp->IoStatus.Status=STATUS_SUCCESS;
pIrp->IoStatus.Information=0;
#ifdef DEBUGMSG
DbgPrint("hi, Starting HelloWorldDispatch()\n");
#endif
IrpStack=IoGetCurrentIrpStackLocation(pIrp);
switch(IrpStack->MajorFunction)
{
case IRP_MJ_CREATE:
#ifdef DEBUGMSG
DbgPrint("hi, IRP_MJ_CREATE\n");
#endif
break;
case IRP_MJ_CLOSE:
#ifdef DEBUGMSG
DbgPrint("hi, IRP_MJ_CLOSE\n");
#endif
break;
case IRP_MJ_DEVICE_CONTROL:
#ifdef DEBUGMSG
DbgPrint("hi, IRP_MJ_DEVICE_CONTROL\n");
#endif
IoControlCodes=IrpStack->Parameters.DeviceIoControl.IoControlCode;
switch(IoControlCodes)
{
case START_HELLOWORLD:
DbgPrint("hi, Starting \"Hello World \"\n");
break;
case STOP_HELLOWORLD:
DbgPrint("hi, Stoping \"Hello World \"\n");
break;
default:
pIrp->IoStatus.Status=STATUS_INVALID_PARAMETER;
break;
}
break;
default:
break;
}
ntStatus=pIrp->IoStatus.Status;
IoCompleteRequest(pIrp,IO_NO_INCREMENT);
return ntStatus;
}
VOID HelloWorldUnload(IN PDRIVER_OBJECT DriverObject) {
UNICODE_STRING DeviceLinkString;
PDEVICE_OBJECT DeviceObjectTemp1=NULL;
PDEVICE_OBJECT DeviceObjectTemp2=NULL;
#ifdef DEBUGMSG
DbgPrint("hi,Starting HelloWorldUnload()\n");
#endif
RtlInitUnicodeString(&DeviceLinkString,DOS_DEVICE_NAME);
IoDeleteSymbolicLink(&DeviceLinkString);
if(DriverObject)
{
DeviceObjectTemp1=DriverObject->DeviceObject;
while(DeviceObjectTemp1)
{
DeviceObjectTemp2=DeviceObjectTemp1;
DeviceObjectTemp1=DeviceObjectTemp1->NextDevice;
IoDeleteDevice(DeviceObjectTemp2);
}
}
}
#endif
=============================makefile========================================
#
#DO NOT EDIT THIS FILE!!!EDIT .\SOURCES. IF YOU WANT TO ADD A NEW SOURCE
#FILE TO THIS COMPONENT.THIS FILE MERELY INDIRECTS TO THE REAL MAKE FILE
#THAT IS SHARED BY ALL THE DRIVER COMPONENTS OF THE WINDOWS NT DDK #
!INCLUDE $(NTMAKEENV)\makefile.def
=============================sources========================================
TARGETNAME=HelloWorld
TARGETPATH=.
TARGETTYPE=DRIVER
SOURCES=HelloWorld.c
好了,所有驱动程序端的代码已经全部录入完毕了,实话说,这比看书的时候要愉悦得多,特别是这么简单明晰的代码~要是所有工作都象这样,嘿嘿,你会感受到幸福吗,边录入边查看MSDN中某些函数的用法,半个小时吧,基本完成这部分的工作。
下面做什么呢,当然接下来就是编译了。在VC6中也可以进行编译,你可以在网上查找相关文章。在这里我使用zhangjie0072文中所说的,使用DDK的build来编译。
如上图所示,点击开始菜单中红圈所表示的命令,进入CMD命令行环境,这个命令执行的是一个批处理文件,查看“WinXP Checked Build Environment”命令的属性,可以看到对应的快捷方式页中的目标为“C:\WINDOWS\system32\cmd.exe /k C:\WINDDK\2600\bin\setenv.bat C:\WINDDK\2600 chk”,即执行的是DDK中的setenv.bat文件,正如文件名一样,这个批处理文件就是为了设置编译环境的。为了能够使得编译生成的驱动程序包含调试信息,所以这里使用Checked方式来编译。查看setenv.bat,里面有很多环境参数设置,如果你确实知道这些参数的含义,你不妨修改它们,但我现在还不敢动。
进入命令行后,进入HelloWorld.c所在的目录,然后键入build开始编译(见下图)。之后会出现什么呢,天知道。我第一次编译的时候,一下提示出现一百多个error,我……KAO~我第一反应就是DDK的设置有问题了,嘿嘿~虽然是命令行的编译方式,但DDK还是提供了足够的错误提示信息,仔细查看,发现
是头文件中少了一空格,使得宏无法被识别,造成连锁反应,看来DDK的设置没有问题,还是自己代码的问题。见下图。
上面的图只是我有意修改了现在的头文件产生的错误。开始时候还真有一百多个error,查看给出错误提示信息,一个一个地解决,多数还都是录入时候的错误。所有问题都PASS了,就是下面这个样子了。
看到红圈中的部分了吗,在D:\CurrentWork\DrvWork\HelloWorld\i386下生成了helloworld.sys。有点爽~第一个驱动程序总算下地了~
接下来该做什么了呢,sys文件有了,我却开始有点茫然了,平时做一个用户模块下的应用程序,编译完了,生成exe文件也好,生成dll文件也好,接下来当然就是运行测试了,可。。驱动程序怎么启动运行呢。作为真正的硬件,系统Pnp管理器会自己创建PDO,然后查找安装与该PDO相关的驱动程序(包括上层的功能驱动和各种过滤驱动),或者提示用户指定驱动程序的位置然后安装。可。。。。。。这个HelloWorld算怎么回事呢,还有一个问题,是先有了驱动程序还是先有DEVICE_OBJECT呢,DEVICE_OBJECT不是在驱动程序的AddDevice或者就直接在DriverEntry中创建的吗,先琢磨琢磨~这是个关键的问题。
嘿~驱动程序是鸡,DEVICE_OBJECT是蛋~
PDO是由总线驱动程序创建的,再由PDO相关的功能驱动和过滤驱动创建各自的FDO和FIDO。应该是这样的吧~好了,我们开始运行吧,先不管注册表和INF什么的,先手工启动驱动程序再说,他们不是说驱动程序就可以看作一服务吗,找一找,查一查~DriverStudio的工具软件包里就有一个工具可以作为启停驱动来用呀。是的,就是我前面提到过的DriverMonitor。
打开DriverMonitor,File菜单
先“Open Driver”,找到HelloWorld.sys的位置,然后顺序点击“Start Driver”“Stop Driver”“Remove Service Entry”,你将看到下面对应的显示。
你确实看到了你在驱动程序中的调试输出"hi, Starting DriverEntry()"和"hi,Starting HelloWorldUnload()"了,说明你的驱动程序确实启动运行了吧。嘿嘿~
还有好看的~运行我上面提到的另一个工具,就是Symbolic Link Viewer。 这是个查看符号链接的工具,在下图你可以看到HelloWorld(也就是\DosDevices\HelloWorld或者\\.\HelloWorld)和\Device\HelloWorld之间有了链接。这个连接名在以后的用户模式测试程序中将会被用到。用户模式测试程序中的CreateFile调用中将会以\\.\HelloWorld作为文件对象参数。
当然你应该知道,你只能在DriverMonitor工具中的“Start Driver”操作之后,而在“Stop Driver”操作之前,才能看到下面图中红圈标示的部分。否则,驱动程序不在运行中,对应的设备都不存在了,相应的链接也是不存在的。
还有一个工具也挺不错的,加深对整体框架的理解是有益的。DDK中的Device Tree工具。
运行Device Tree,出现下面的画面
接下来创建用户模式下的测试程序吧~
在VC6中使用向导创建一个Win32 Console Application的空项目,命名为HelloTest,位于
D:\CurrentWork\DrvWork\HelloWorld下,其实项目下就一个主文件HelloTest.cpp。使用VC6的向导生成一
个项目,可以避免设置编译参数,如果单独编译HelloTest.cpp,编译能通过,但连接时总报错误,我没能
解决,只能绕过。但在项目设置中(Project\Setting)要注意一点,不能使用预编译头文件。如下图。
下面是HelloTest.cpp源文件
==================================HelloTest.cpp====================================
#define DEBUGMSG
#include
#include
#include
#define DEVICE_HELLO_INDEX 0x860
#define START_HELLOWORLD
CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX,METHOD_BUFFERED,FILE_ANY_AC
CESS)
#define STOP_HELLOWORLD
CTL_CODE(FILE_DEVICE_UNKNOWN,DEVICE_HELLO_INDEX+1,METHOD_BUFFERED,FILE_ANY_
ACCESS)
#define erron GetLastError()
#define MY_DEVICE_NAME "\\\\.\\HelloWorld"
#define MY_DEVICE_START "-start" #define MY_DEVICE_STOP "-stop"
BOOL DriverControl(TCHAR * Maik);
void Usage(TCHAR * Parameter);
int main(int argc,TCHAR *argv[]) {
if(argc!=2)
{
Usage(argv[0]);
return 0;
}
if(strcmpi(argv[1],MY_DEVICE_START)==0 || strcmpi(argv[1],MY_DEVICE_STOP)==0)
DriverControl(argv[1]);
else
{
Usage(argv[0]);
return 0;
}
return 0;
}
BOOL DriverControl(TCHAR * Maik) {
HANDLE hDevice=NULL;
DWORD RetBytes=0;
hDevice=CreateFile(MY_DEVICE_NAME,GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXIST
ING,FILE_ATTRIBUTE_NORMAL,NULL);
if(hDevice==INVALID_HANDLE_VALUE)
{
#ifdef DEBUGMSG
printf("CreateFile() GetLastError reports %d\n",erron);
#endif
return FALSE;
}
if(strcmpi(Maik,MY_DEVICE_START)==0)
{
if(!(DeviceIoControl(hDevice,START_HELLOWORLD,NULL,0,NULL,0,&RetBytes,NULL)))
{
#ifdef DEBUGMSG
printf("DeviceIoControl() GetLastError reports %d\n",erron);
#endif
CloseHandle(hDevice);
return FALSE;
}
else
printf("start success!");
}
if(strcmpi(Maik,MY_DEVICE_STOP)==0)
{
if(!(DeviceIoControl(hDevice,STOP_HELLOWORLD,NULL,0,NULL,0,&RetBytes,NULL)))
{
#ifdef DEBUGMSG
printf("DeviceIoControl() GetLastError reports %d\n",erron);
#endif
CloseHandle(hDevice);
return FALSE;
}
else
printf("stop success!");
}
if(hDevice)
CloseHandle(hDevice);
return TRUE;
}
void Usage(TCHAR * Parameter)
{
fprintf(stderr,"================================================\n%s -start\t 启动\n%s -stop \t 停止\n ===============================================\n",Parameter,Parameter);
}
编译通过,在.\debug下生成EXE文件,在命令行中运行该程序,同时查看DriverMonitor窗口的显示,看
看有什么不同。同时启动或停止驱动程序,也看看HelloTest有什么不一样的输出。
最后一部分就是SOFTICE的调试工作了
调试之前,首先得启动Softice,再运行SOFTICE 的Symbol Loader来装载符号文件,在Symbol Loader中打开HelloWorld.sys,并Translate和Load,一切正常的话,应该有下图显示的状态。
这时候就可以CTRL,D,调出Softice,调用file helloworld.c,显示helloworld.c文件,用bpx命令下几个断点,比如:bpx DriverEntry,bpx HelloWorldDispatch等等,没办法,这一部分无法截图了。
下面就可以使用DriverMonitor和HelloTest来启动驱动、停止驱动和发送IRP,我想,如果一切正常的话,你应该可以在你设置的相应断点处,如期地把Softice弹出来了~
完了,到这完了吧~我想一条完整的通道算是淌过来了。写了一天了,实在是有点顶不住了,硬撑着算是写完了,留到明天我估计就黄了,算是给自己做个学习笔记吧。