WDM驱动程序设计 一.WDM简介 微软不断推出新的操作系统,现在Windows98和Windows2000已经成了主流,原先用来实现驱动程序的VxD技术随着Win95的淡出也慢慢地将退出历史舞台,在Windows98和Windows2000中设备驱动程序将根据Windows驱动程序模型(WDM)来设计。WDM通过提供一种灵活的方式来简化驱动程序的开发,在实现对新硬件支持的基础上减少并降低所必须开发的驱动程序的数量和复杂性。 Windows驱动程序模型分两个方面,除了核心模型描述设备驱动程序的标准结构外,WDM还为常见类型的设备实现了一个模块化的、分层次类型的总线驱动程序和类驱动程序。总线驱动程序实现了支持通用串行总线(USB)、IEEE1394(FireWire)协议等。类驱动程序是为实现标准Windows功能提供条件。WDM对标准类接口的支持减少了Windows 95和Windows NT所需的设备驱动程序的数量和复杂性。在Windows平台上,WDM将成为21世纪主流的驱动模式。 WDM支持USB、IEEE 1394、ACPI等全新的硬件标准。而且以往在两个平台上同时运行时需要编写两个截然不同的驱动程序,现在只需要编写一个WDM驱动程序就可以了。WDM驱动程序也是分层的,即不同层上的驱动程序有着不同的优先级,而Windows 9x下的VxD则没有此结构。 ![](http://www.host01.com/UpLoadFiles/UpPic/2006-9/17/0691723121412759.gif) WDM还引入了功能设备对象FDO(Functional Device Object)与物理设备对象PDO(Physical Device Object)两个新类来描述硬件,一个PDO对应一个真实硬件。一个硬件只允许有一个PDO,却可以拥有多个FDO,在驱动程序中直接操作的不是硬件而是相应的PDO与FDO。在用户态和内核态通讯方面,系统为每一个用户请求打包形成一个IRP结构,将其发送至驱动程序,并通过识别IRP中的PDO来区别是发送给哪一个设备的。另外,在驱动程序的加载方面,WDM不通过驱动程序名称识别,而是通过一个128位的全局唯一标识符GUID来实现驱动程序的识别。我们用上图来说明设备驱动程序的分层及调用。 写WDM和其它模式驱动程序基本上是相同的,代码中的主要区别在于如何创建设备。 在WDM驱动程序中,即插即用(PnP)管理器告知何时向系统添加一个设备,或者从系统删除设备。PnP管理器使用安装的INF文件查找新设备的正确驱动程序;而其它模式驱动程序必须发现它自己的设备,使用专门的安装程序安装。 另外在细节上也存在很多区别,其它模式驱动程序参数一般由注册表提供,在DriverEntry里调用读注册表的函数,然后根据注册表再调用CreateDevice,但是WDM一般不是这样,这是由于Windows 2000下支持PnP,在加载的时候PnP管理器调用AddDevice入口点创建设备。一般在DriverEntry里创建的是一个与设备或者对象毫无关系的虚拟设备,用于管理与Win32的通讯。如果不想对该设备做什么特别的处理,或者设备不复杂,AddDevice可以简单返回Nt_Success,不用调用CreateDevice。 另外整个设备驱动树也发生了改变,从而使安装程序发生了很大的改变。WDM本身的PNP管理器被抽象地提升到了ROOT的地位。PNP管理器负责所有的总线驱动程序的加载。总线驱动程序则负责遍历所有位于总线上的设备,并且为每个设备创建相应的设备对象。当PNP管理器发现一个设备对象,就查找该对象对应的Driver。并调用该Driver的ADD DEVICE例程。如果Driver不在内存中,就先加载,然后调用ADD DEVICE例程。 当然,总线本身并没有发出任何信号告诉PNP管理器自己的存在,所以,总线Driver是在NT的安装时设定的。而ISA设备并没有规范,因为需要KMD自己检查硬件存在及状态,所以它是老式KMD存在的惟一理由。这也是微软极力在新规范里取消ISA总线的理由之一。WDM支持PNP协议和PM协议,而且实现时仅仅需要在MAJOR FUNCTION里加入一些对PNP和PM事件响应的例程即可。 一个完整的驱动程序要完成以下工作:初始化;创建与删除设备;处理应用层程序的打开和关闭句柄的请求;处理应用层程序的输入/输出请求;串行化对设备的访问;访问硬件;调用其他驱动程序;取消I/O请求;超时I/O请求;处理可热插拔设备的加入和删除事件;电源管理和WMI。
二.开发设备驱动程序
2.1设计工具DDK的安装
编写WDM设备驱动程序我们需要Microsoft的驱动程序开发工具包DDK。尽管微软宣称WDM驱动程序是Windows98和2000之间二进制兼容的,为了安全起见我们还是在不同的系统下仅安装相应的DDK。
(一).Windows 98 DDK的安装 这一部分描述如何安装Windows 98 DDK。我们约定%98DDK%为安装Windows 98 DDK的根目录;%MSTOOLS%为微软SDK平台的根目录;%VCPPDEV%为安装的VC++开发环境的根目录。 使用DDK的软件平台通常为Windows 98操作系统和VC++ 4.2或5.0版本,若要编译视频捕获示例则需要VC++ 5.0版本。你必须在安装Windows 98 DDK之前先安装VC++编译/开发环境,否则运行时,Windows 98 DDK的Setenv.bat文件将不能建立正确的编译环境 。阅读DDK 文档需要IE 4.01以上版本,如果是从光盘或需要测试光盘驱动程序则需要光驱,16MB内存是不可少的,完整安装需要82MB硬盘空间(最小安装需要32MB硬盘空间)。 Windows 98 DDK所带的所有驱动程序例子都不需要在安装有SDK的平台下构造。不过,如果你开始开发自己的驱动程序,可能需要Windows 98 DDK中没有而是在SDK平台中的头文件,因此可以考虑两种方法:拷贝所需的头文件或SDK平台的文件到在%98DDK%和%VCPPDEV%下适当的Include 目录中;也可以直接安装SDK平台,编辑%98DDK%/BIN中Setenv.bat文件,并运行安装在%MSTOOLS%里的Setenv.bat文件。 用Setup程序安装DDK,步骤为: (1)运行Windows 98 DDK中的SETUP.EXE文件,按照对话框提示安装Windows 98 DDK到%98DDK%。 (2)安装VC++ 5.0到%VCPPDEV%。 (3)修改CONFIG.SYS增大环境变量空间。在CONFIG.SYS文件最后加入一行: SHELL=C:/WINDOWS/COMMAND.COM /p /e:4096 在安装Windows 98 DDK之前,必须先安装VC++编译器/开发环境,否则运行时,Win dows 98 DDK的Setenv.bat批处理文件将不能建立正确的环境。 下面介绍建立Windows 98驱动程序构造环境以及利用构造环境和工具构造驱动程序的方法。 1. 用SETENV.BAT来安装驱动程序构造环境 开始菜单中有"Development Kits/Windows 98 DDK"的目录。这个目录包括自由构造环境项和检查构造环境项。每次重启操作系统,在构造驱动程序前,单击这些程序文件夹中合适的一项。这些项调用%98DDK%/BIN里的Setenv.bat批处理文件来创建正确的环境变量的驱动程序构造环境。
2. 手工运行SETENV.bat 在MS-DOS提示符下,或在"开始/运行"中使用下列语句: setenv %98DDK% [free | checked] 例如,在C:/98ddk/bin〉提示符下,键入setenv C:/98ddk free,其中第一个参数指定DDK被安装的文件夹,注意缺省安装是/98ddk;可选的第二个参数说明目标构造环境,缺省类型是free。
3. 构造WDM驱动程序 使用一系列规则以指定驱动程序怎样被创建,构造实用程序可用来在Windows 98 和Windows NT平台上构造WDM驱动程序。 在Windows 98 DDK被安装之后,WDM驱动程序构造树的工作例子和组成部分文件在硬盘上就可以得到了。驱动程序构造树根目录在%98DDK%/src。查看%98DDK%/inc里Makefile.def文件的内容,以及贯穿驱动程序构造树的各种的Dirs文件和源文件的内容,可以利用这些代码作为工作实例。
4.构造驱动程序 在当前目录的驱动程序构造树中创建一个子目录,然后,运行构造实用程序。在构造树的当前目录中,构造实用程序可以自动创建出驱动程序的源代码。构造实用程序在Windows 98 DDK例子驱动程序构造树的根目录下(%98DDK%/src)运行。例如,如果仅仅对为声音设备类构造的例子驱动程序有兴趣,可以设置当前目录到%98DDK%/src/audio上,然后,运行构造实用程序。
5.检查Windows 98 DDK的安装 经常使用的构造指令形式为build -cZ ;从而使构造实用程序做相关文件的扫描,执行完整的创建,并生成错误记录。检查安装的方法为:在/〈destination〉/src目录运行build -cZ,构造安装的例子驱动程序源代码的完整集。这个实用程序在构造驱动程序之前构造全部相关文件,自动建立文件关联关系。这个过程可能需要30多分钟。如果构造没有完成或报告过多的编译错误,则需要确认是否正确执行了以上的安装步骤。通过安装DDK和相应的开发软件,我们构造好了WDM驱动程序的开发环境。接着,我们就要深入进行设计与开发工作了。 安装DDK后,在DDK程序组下有检查Check和自由Free两个编译环境,Check环境用于编译带调试信息的驱动程序,Free则是编译正式发布版本的环境。通常情况下设备驱动程序的编译采用命令行的方式。通过一定的设置可以在VC ++的集成环境下编译。 一般来说,成功编译一个最基本的设备驱动程序需要四个文件,第一个是驱动程序,即C语言源程序文件(例如isousb.c,注意下面所有的例子都是以isousb来说明);第二个是RC文件(例如isousb.rc);第三个是SOURCES文件;第四个文件是MAKEFILE文件。SOURCES文件和make文件类似,用来指定需要编译的文件以及需要连接的库文件。这三个辅助文件都很简单,在DDK samples的每个例程里都有三个这样的文件,依样画瓢就能理解它们的结构和意义。
1.举例分析 以下以isousb程序为例,设isousb.rc代码为: #include <windows.h> #include <ntverp.h> #define VER_FILETYPE VFT_DLL #define VER_FILESUBTYPE VFT2_UNKNOWN #define VER_FILEDESCRIPTION_STR "I82930 Isochronous IO Test Driver" #define VER_INTERNALNAME_STR "IsoUsb.sys" #define VER_ORIGINALFILENAME_STR "IsoUsb.sys" #include "common.ver" 设备驱动程序一般都使用Build实用程序来进行,Build只是NMAKE外面的一个外包装程序。Build本身其实相当简单,编译的大部分工作实际上由Build传递给NMAKE来进行。
/SOURCES/ TARGETNAME=IsoUsb TARGETTYPE=DRIVER TARGETPATH=$(BASEDIR)/LIB DRIVERTYPE=WDM INCLUDES=$(BASEDIR)/inc; / $(BASEDIR)/src/usb/inc; / $(BASEDIR)/src/wdm/usb/inc; / .. /../inc TARGETLIBS=$(BASEDIR)/lib/*/free/usbd.lib USE_MAPSYM=1 SOURCES= / IsoUsb.rc / IusbDbg.c / IsoUsb.c / IsoPnP.c / IsoPwr.c / IoctlIso.c / IsoStrm.c / OcrwIso.c /end of SOURCES/ 注意SOURCES的文件名没有任何扩展名。 # makefile ########################################################################### # # Copyright (C) Microsoft Corporation 1995 # All Rights Reserved. # # MAKEFILE for WDM device driver kit # ########################################################################### # # 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 # end of makefile 对所有驱动程序而言,makefile都是一样的,Microsoft也警告不要编辑这个文件,如果需要,可以编辑修改sources文件达到同样的效果。对于设备驱动程序,所使用的C编译器基本上无一例外地选用VC++。
2.编译的基本步骤 (1)首先进入check或free编译环境,初始化DDK编译环境。 (2)运行VC安装目录下bin目录下的vcvars32.bat,初始化VC++编译环境。 (3)运行Build.exe进行编译。 (二).Windows2000 DDK的安装
由于前面我们已经详细介绍了Windows98 DDK的安装,我们在这里主要介绍一下Windows2000 DDK安装与Windows98 DDK的不同。 两者对系统的要求不同,Windows2000 DDK需要Windows2000或Windows98操作系统,VC++5.0或6.0专业或企业版,至少64MB内存,推荐128MB或更多的内存,完全安装需要200MB,而且如果你在Windows2000下安装的话,必须以管理员的身份登录。
2.2设备驱动程序的设计 I/O请求包(IRP)是驱动程序操作的中心,IRP是一个内核对象,它是一个预先定义的数据结构,带有一组对它进行操作的I/O管理器例程。I/O管理器接收一个I/O请求后分配并初始化一个IRP。一个IRP有一个固定的首部和可变数目的IRP栈单元块,每个I/O请求有一个主功能代码(IRP_MJ_XXX)并可能有次功能代码(IRP_MN_XXX)。设计一个设备驱动程序,应该支持和其他相同类型设备的驱动程序相同的IRP_MJ_XXX和IOCTL请求代码。如果设计一个中间层驱动程序,应该首先确认下层驱动程序所管理的设备,因为一个高层的驱动程序必须具有低层驱动程序绝大多数IRP_MJ_XXX例程入口。高层驱动程序在接到I/O请求时,在确定自身IRP当前堆栈单元参数有效的前提下 ,设置好IRP中下一个低层驱动程序的堆栈单元,然后再调用IoCallDriver将请求传递给下层驱动程序处理。一旦决定好了驱动程序应该处理哪些IRP_MJ_XXX,就可以开始确定驱动程序应该有多少个Dispatch例程。当然也可以考虑把某些IRP_MJ_XXX处理的例程合并为同一例程处理。 一个驱动程序必须为它所管理的每个可能成为I/O请求的目标的物理和逻辑设备创建 一个Device对象。一些低层的驱动程序还可能要创建一些不确定数目的Device对象。例如一个硬盘驱动程序必须为每一个物理硬盘创建一个Device对象,同时还必须为每个物理磁盘上的每个逻辑分区创建一个Device对象。 一个高层驱动程序必须为它所代表的虚拟设备创建一个Device对象,这样更高层的驱动程序才能连接它们的Device对象到这个驱动程序的Device对象。另外,一个高层驱动程序通常为它低层驱动程序所创建的Device对象创建一系列的虚拟或逻辑Device对象。 尽管可以分阶段来设计驱动程序,从而使一个处在开发阶段的驱动程序不必一开始就创建出所有它将要处理的所有Device对象,但从一开始就确定好最终要创建的所有Device对象将有助于设计者所要解决的任何同步问题。另外,确定所要创建的Device对象还有助于定义Device对象的Device Extension的内容和数据结构。 驱动程序的开发是一个从粗到细逐步求精的过程。DDK的src/目录下有一个庞大的模板代码,几乎覆盖了所有类型的设备驱动程序、高层驱动程序和过滤器驱动程序。在开始开发驱动程序之前,可以先在这个样板库下面寻找是否有和所要开发的类似类型的例程。 下面笔者将进一步介绍开发驱动程序的基本步骤:
l.编写驱动程序框架 (1)首先编写一个DriverEntry例程,并在该例程里调用IoCreateDevice来创建一 个Device对象。在这个例程中必须设置一系列的回调(callback)例程来处理IRP。 (2)写一个处理IRP_MJ_CREATE请求的例程的基本框架。如果驱动程序创建了多于一个的Device对象,则必须为IRP_MJ_CLOSE请求写一个例程。 (3) 编译连接驱动程序。
2.测试驱动程序 (1)首先在系统中安装好驱动程序。 (2)为逻辑设备名称和目标Device对象名称之间建立起符号链接,在前面已经知道Device对象名称对Win32用户模式是不可见的,是不能直接通过API来访问的,Win32 API只能访问符号链接名。可以通过修改注册表来建立这两种名称之间的符号链接。运行Regedt32.exe在/HKEY_LOCAL_MACHINE/ System/ CurrentControlSet/ Control/ Session Manager/ DOS Devices下建立起符号链接,创建这种符号链接也可以在驱动程序里调用函数IoCreateSymbolicLink,使用参数传递合适的符号链接名和内核设备名。 (3)完成以上所有的设置并检查无误后,我们必须重新启动Windows系统。 (4)编写一个简单的测试程序调用Win32 API中的CreateFile函数,并以符号链接名打开这个设备。如果打开成功,则成功地写出了一个最简单的驱动程序了。支持更多的设备I/O请求,例如驱动程序可能需要对IRP_MJ_READ请求做出响应(完成后可用ReadFile 函数进行测试)。如果驱动程序需要能够手工卸载,那么还必须对IRP_MJ_CLOSE做出响应。为所需要处理的IRP_MJ_XXX写好处理例程,并在DriverEntry里面初始化好这些例程入口。一个低层的驱动程序需要一个StartIo、ISR和DpcForIsr例程,可能还需要一个SynchCritSection例程,如果设备使用了DMA,那么可能还需要一个AdapterControl例程。 对于高层驱动程序可能需要一个或多个IoCompletion例程,最起码完成检查I/O状态 块然后调用IoCompleteRequest的工作。如果需要,还要对Device Extension数据结构和内容做些修改。有一点必须很清楚的,就是代码运行级别的问题,即IRQL,最常见的级别是PASSIVE_LEVEL、APC_LEVEL、DISPATCH_LEVEL和DIRQL。 在查阅DDK HELP中的函数说明的时候,要注意函数的可运行级别,比如有的函数只能在PASSIVE_LEVEL下运行,有的函数则可以在DISPATCH_LEVEL以下级别运行,级别越高的时候,对代码的要求就越严格,比如在DISPATCH_LEVEL的时候,就不能使用分页内存。通常情况下应该尽可能让代码在低运行级别如PASSIVE_LEVEL下运行,在高级别下运行过长时间将导致系统效率降低、影响系统响应的实时性。但有时候自己无法控制运行的级别,例如在调用低层Driver时使用IoCallDriver,低层Driver响应完毕后会执行completion例程,该例程运行的级别就是由低层Driver来决定。因此在编写completion例程时,应尽量将这个函数设计成能在DISPATCH_LEVEL级别运行。 依照以上开发步骤,我们可以设计出全新的WDM设备驱动程序。
2.3设备驱动程序的安装
(一). WDM驱动程序安装 驱动程序根据INF文件的指令进行安装,将可执行文件复制到正确的位置,并创建各种注册表项。一些驱动程序需要占用一些硬件资源,主要是I/O地址和中断号,PnP管理器将予以分配。使用后的INF文件复制到Windows INF子目录。 INF文件含有安装一个WDM设备驱动程序需要的所有信息,包括要复制的文件、要创建的注册表项等。INF文件是一个文本文件,它由节组成,每个节以方括号内的节名称开始,以后每一行都是一个简单的项,或设置一个值。下面我们以DDK中的一个例子简单介绍一下INF文件。 BULKUSB.INF [Version] Signature="$CHICAGO$" Class=USB provider=%MSFT% LayoutFile=layout.inf [SourceDisksNames] 1="DDK BULKUSB Sample","",1 [SourceDisksFiles] bulkusb.sys=1 [Manufacturer] %MfgName%=Microsoft [Microsoft] %USB/VID_045E&PID_930A.DeviceDesc%=BULKUSB.Dev, USB/VID_045E&PID_930A [PreCopySection] HKR,,NoSetupUI,,1 [DestinationDirs] BULKUSB.Files.Ext = 10,System32/Drivers BULKUSB.Files.Inf = 10,INF [BULKUSB.Dev] CopyFiles=BULKUSB.Files.Ext, BULKUSB.Files.Inf AddReg=BULKUSB.AddReg [BULKUSB.Dev.NT] CopyFiles=BULKUSB.Files.Ext, BULKUSB.Files.Inf AddReg=BULKUSB.AddReg [BULKUSB.Dev.NT.Services] Addservice = BULKUSB, 0x00000002, BULKUSB.AddService [BULKUSB.AddService] DisplayName = %BULKUSB.SvcDesc% ServiceType = 1 ; SERVICE_KERNEL_DRIVER StartType = 2 ; SERVICE_AUTO_START ErrorControl = 1 ; SERVICE_ERROR_NORMAL ServiceBinary = %10%/System32/Drivers/BULKUSB.sys LoadOrderGroup = Base [BULKUSB.AddReg] HKR,,DevLoader,,*ntkern HKR,,NTMPDriver,,BULKUSB.sys HKLM,"System/Currentcontrolset/Services/BulkUsb/Parameters","MaximumTransferSize",0x10001,4096 HKLM,"System/Currentcontrolset/Services/BulkUsb/Parameters","DebugLevel",0x10001,2 [BULKUSB.Files.Ext] BULKUSB.sys [BULKUSB.Files.Inf] BulkUsb.Inf ;---------------------------------------------------------------; [Strings] MSFT="Microsoft" MfgName="Intel" USB/VID_045E&PID_930A.DeviceDesc="BulkUsb.Sys Intel 82930 USB Bulk IO Test Board" BULKUSB.SvcDesc="BulkUsb.Sys i82930 Bulk IO test driver" 我们介绍一下常用的节: [Version] Signature项为$Windows NT$,$Windows 95$或$Chicago$; Class项为系统定义的一个类名字(例中为USB)或用户指定的新的类名字; Provider项为INF文件的创建者; [SourceDisksNames] 对于每个发行软盘或CD-ROM,指定它的描述和可能的打包文件和目录; [SourceDisksFiles] 指定文件名、源磁盘ID和可选的子目录和文件大小,如果所有文件都在根目录下这个节可以为空; [Manufacturer] 指定厂商名; [DestinationDirs] 对缺省文件复制和指定文件复制指定目录; [BULKUSB.Dev] 指定要复制文件的列表节(BULKUSB.Files.Ext, BULKUSB.Files.Inf)的名称;指定addreg节(BULKUSB.AddReg)的名称; [BULKUSB.AddService] 指定驱动程序服务的详细信息; [BULKUSB.AddReg] 在注册表中添加新的键值; [Strings] 指定一些字符串; (二).NT式驱动程序安装 1.添加注册表中的键值 Windows在引导的时候,通过扫描注册表构造驱动程序列表。这个列表既包括自启动的驱动程序,也包括需要手工启动的驱动程序。这个列表其实就是控制面板中设备 Applet所列出来的所有设备。所有的设备驱动程序应该在注册表的HKEY_LOCAL_MACHINE /System/CurrentControl-Set/Services/下有相应的键值。这里的名称应该和你的驱动程序名称一致。下面一般有以下键值:
![](http://www.host01.com/UpLoadFiles/UpPic/2006-9/17/0691723121493408.gif)
Type值为1表示内核模式驱动程序;为2表示文件系统驱动程序。 ErrorControl值为0表示日志记录错误并忽略;值为1表示日志记录错误并显示一个 对话框;值为2表示日志记录错误,并用最后的正确配置重新启动;值为3表示日志记录 错误,如果已经使用过正确配置,返回失败。 在任何一个设备驱动程序中,上表中的前三项参数都是必需的。
2.控制驱动程序的装入次序 有时候控制多个驱动程序的装入次序是必要的。有时一套驱动程序中的部分程序必须在其他程序启动的前提下启动。
3.驱动程序的Start值 上面注册表中驱动程序的Start值控制驱动程序在系统启动的时间。目前,Start可以取以下值,此外为该值留有扩展余地,以适用于新的要求: (l)0x0 (SERVICE_BOOT_START):这个值指定本驱动程序应该由操作系统装入程序启 动。一般的驱动程序不会采用本值,因为系统在这个时候几乎还没有启动,大部分系统尚不可用。 (2)0x1 (SERVICE_SYSTEM_START):该值表示在操作系统装入后但同时初始化它自己时启动驱动程序。 (3)0x2 (SERVICE_AUTO_START):该值表示在整个系统启动并运行后由服务控制管理器装入。 (4)0x3 (SERVICE_DEMAND_START):该值表示该驱动程序必须手工启动。可以通过控制面板的设备applet或者使用WIN32 API编程来启动。 (5)0x4 (SERVICE_DISABLED):表示本驱动程序被禁用。 注意在调试驱动程序的时候,最好将Start值设置为3来手工启动,这是因为如果设置为自动启动,而驱动程序在启动的过程中又发生了异常错误的话,可能导致系统不能启动。 如果没有紧急恢复盘,首先可以尝试在启动的时候选择用已知的配置来启动系统,看是否能启动成功。如果失败,可以用DOS启动后到/%SystemRoot%/System32/Drivers目录下将出现问题的驱动程序删除,然后系统就可以启动了。 通过设置Start可以控制驱动程序在不同的时候启动。但如果要解决依赖性问题,则需要使用Group和DependOnGroup值。 首先要确定自己的驱动程序使用的Group名,系统有一些定义好的组名,对于当前系统存在的组名,可以观察注册表的/HKEY_LOCAL_MACHINE/System/CurrentControl- Set/Control/ServiceGroupOrder/List的键值。例如该值可以设置为: … SCSI miniport port Primary disk SCSI class SCSI CDROM class filter boot file system … 这里每一行都是一个Group名,一般来说某个驱动程序都属于某一个Group。系统启动时按照该List下组的顺序依次启动各组里的驱动程序。DependOnGroup值控制本驱动程序启动的时候必须先启动另一组的驱动程序。
4.修改注册表的方法 在注册表里这些值可以手工修改,也可以自己编程利用WIN32 API进行添加,同时也 可以用ini文件的方式来添加。然后以ini文件名为参数运行REGINI.EXE。就会自动在注册表里添加相应的项。在注册表里添加好这些项后,必须重新启动系统,这样所添加的设备驱动程序才能在控制面板的设备applet中列出来,再进行其他操作。
5.启动设备驱动程序 在添加修改好注册表后,重新启动系统,如果选择的Start值是0、1、2,如果一切正常,驱动程序就应该已经启动起来了。可以观察控制面板的设备applet中的设备列表。如果Start选择的是3,则可以直接启动。
6.调试 由于驱动程序的运行级别较高,我们不能将一个没有经过严格测试的程序交给用户使用。驱动程序的测试可以通过在程序中定义一些输出的宏来完成,也可以通过一些工具进行源代码级的调试。有一些跟踪工具可以帮助我们,如Compuware Numega的Driver Monitor和Open System Resources的OSRTracer。DDK还提供了WinDbg调试程序,不过要使用这个程序需要两台通过串行电缆连接或网络连接的PC机,分别运行检查构造和自由构造。上面提到的Numega还提供SoftICE调试程序,相信有不少人用它来破译软件密码吧。 至此我们已经粗略的介绍了WDM程序的设计方法,在实际的编写过程中我们可以借助于DDK的文档和示例来协助我们编写属于我们自己的驱动程序。 |