wdf简介2--helloworld驱动开发
好文章,来自【福优学苑@音视频+流媒体】
1 简述
从今天开始,我们开始学习Windows下的驱动开发。与时俱进,我们采用的是WDF(Windows Driver Foundation)驱动框架。一方面,该框架对于简化了驱动,提供了PNP和电源管理的默认实现,使得我们暂时不必关注一些复杂的东西,简化了学习难度。另一方面,我们本着由浅入深的原则,一开始对于驱动框架中的东西不必要所有的都弄懂,先有一个概念,随着学习的深入,自己动手实践,有些东西慢慢地就理解了。
2 为什么选择Hello World
老实说,对于选择什么程序作为第一个学习的驱动程序,一直纠结于是否要用Hello World。最后还是决定使用它,一方面,这绝对是最简单的,简单到大家都不愿意看,也给刚刚起步的人一个驱动的直观印象。另一方面,也是最重要的,那就是第一个程序的重要性不在于这个程序本身。更重要的作用是帮助我们磨刀,我们要让这个简单的KMDF程序从编写到编译成功,并成功的安装运行。熟悉整个过程,熟悉工具的使用,毕竟,实践才是学习的最好方法。
Hello Word驱动源代码编写
这个驱动的功能就是在设备被安装的时候打印出“Hello World!”字符串,在驱动被卸载的时候打印出“Thanks For Using!”字符串。顺便说一下,对于代码,我并不会一句一句地讲那么详细,因为网上并不缺乏驱动教程。你在看本教程之前,应该先去看看相关的教程和书籍,本教程的使用的例子基本上都来自于相关书籍和教程。那么,我写下这个教程有什么意义?一方面,学习是需要整理的,经常需要理一理自己学过的东西,有助于自己进步,所以我把它整理出来便于自己学习进步。另一方面,网上的教程和书籍一下子灌输了太多的东西,看着看着就迷失了。在内核下写程序,很重要的功力就是对于内核的了解,但一开始我们知道的甚少,一下子灌输内核的许多东西会造成很难学习,迷失不知怎么进行下一步。而且,这些书籍中的实例对于初学者过于复杂,没有循序渐进的过程,难免打击学习信心。所以我在教程中尽量简化,一步一步深入,慢慢地体会驱动开发。这也是我自己的学习过程。
回到我们的Hello World驱动,再编写它之前,你应该对于设备堆栈和设备安装和移除的过程有一些了解,你可以看看《Windows 7设备驱动程序开发》第7章“即插即用和电源管理”的部分内容。在设备插入时,框架会调用EvtDriverDeviceAdd回调函数,在该函数中创建设备对象。驱动卸载时,框架调用EvtDriverUnload回调函数,所以,我们只需要自己实现这两个回调函数,然后向框架注册即可。
下面是完整的代码:
#include <ntddk.h>
#include <wdf.h>
// 添加设备回调函数
NTSTATUS EvtDriverDeviceAdd( IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit);
// 驱动卸载回调函数
VOID EvtDriverUnload( IN WDFDRIVER Driver );
NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath )
{
NTSTATUS status;
WDF_DRIVER_CONFIG config;
// 注册EvtDriverDeviceAdd回调函数
WDF_DRIVER_CONFIG_INIT(&config, EvtDriverDeviceAdd);
// 注册EvtDriverUnload回调函数
config.EvtDriverUnload = EvtDriverUnload;
// 创建驱动对象
status = WdfDriverCreate( DriverObject, RegistryPath,
WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE);
return status;
}
NTSTATUS EvtDriverDeviceAdd(IN WDFDRIVER Driver,
IN PWDFDEVICE_INIT DeviceInit)
{
NTSTATUS status;
WDFDEVICE hDevice;
PAGED_CODE();
// 创建设备对象
status = WdfDeviceCreate(&DeviceInit, WDF_NO_OBJECT_ATTRIBUTES, &hDevice);
if( !NT_SUCCESS(status) )
{
KdPrint(("EvtDriverDeviceAdd failed.\n"));
return status;
}
KdPrint(("Hello World!\n"));
return status;
}
VOID EvtDriverUnload( IN WDFDRIVER Driver )
{
KdPrint(("Thanks For Using!\n"));
}
5 编译Hello World驱动
在编译驱动之前,除了源代码(.h和.c),还要一些用于编译的其它文件。这部分内容需要先涉及一些驱动安装相关的知识,了解驱动安装运行需要哪些文件,我们才好准备这些内容。这部分内容最完整的资料是WDK文档和MSDN文档,下面将一些重要内容做简单介绍,但是更详细的内容,读者还是要自己看文档。
MSDN给出了驱动开发和安装的七个步骤,如下:
了解Windows设备和驱动安装的基础知识。
了解驱动包及其组成。
了解INF文件。
创建你自己的驱动包。
开发和测试过程中给你的驱动包进行测试签名。
给你的驱动包进行发布签名。
发布驱动包。
也就是说,我们最后开发的所有东西都通过一个驱动包发布出去,商业的驱动包最后都打包成一个.exe,引导用户一步一步安装完成即可。最开始学习时,不需要做到这一步,但是我们需要提供一个最简单的驱动包。虽然不需要提供自安装功能,起码要包含安装运行所需要的所有内容。
根据MSDN,驱动包中包含以下文件:驱动文件、安装文件以及其它文件。其中驱动文件是由我们所写的源代码编译而成的.sys动态库,安装文件包含INF文件和签名文件(.cat),其它文件则是一些说明文件。测试签名本来是应该做的,但是这个过程并不简单,对于初学者来说,写INF文件已经是一个繁琐复杂的过程了。所以我们暂时先忽略它,相关的内容以后单独作一节来写。
现在我们就明确编译阶段的任务,生成.sys驱动,INF安装文件。与WDK中的示例一样,现在我们要编写几个文件:makefile,makefile.inc,sources,和INX。
Makefile和makefile.inc文件直接从示例中拷贝即可,基本保持不变,如果读者想弄清楚具体内容,需要知道makefile的相关知识。至于其中的一些宏定义,WDK中bin目录下有一个setenv.bat文件,从快捷方式打开WDK编译环境时会运行这个批处理文件(如图5.1),设置环境变量,这个文件中有一部分宏定义。
图5.1
下面的重点是INX文件的编写。INX文件实际上是一个包含字符串变量的INF文件,其中的字符串变量表示了版本信息,通过一个INX文件可以编译出用于不同平台和框架版本的INF文件。编译它的工具为Stampinf.exe,位于WDK中bin目录下。关于INF文件的中文最详尽清楚的教程,应该是张佩、马勇和董鉴源编著的《竹林蹊径 深入浅出Windows驱动开发》,里面对于驱动安装和测试有比较详细地描述。读者应该先看一下该书及相关文档。而且,读者一定要去看看该书,应用我的一些术语说法引用自该书,书中解释得很清楚,我只是教大家写一个完整的INX文件。
那么我们直接开始编写INX文件了,编写的过程中请参考WDK示例。此处说一下,第一个INX文件不要直接拷贝示例然后修改,最好从一个空白文件一行一行写起。你只有知道一个INX文件包含哪些部分,每部分如何组成的,以后拷贝出问题后才知道怎么修改。
INX文件与ini文件格式类似,因此,可以新建一个HelloWorld.ini文件,便于在Notepad++中提供语法高亮,阅读起来更容易,一般INX文件与驱动同名。不过,ini的注释为单行,而INX文件中的注释不需要另起一行。
先添加版本域,这也是一个必选的域:
----------------------------------------------------------------------------------
; 版本域
[Version]
Signature="$WINDOWS NT$" ; 固定
Class=HUSTSample ; 我们的驱动不属于已有的设备类,定义一个新的设备类
ClassGuid={FDA3877E-5FF3-4c18-8235-7FEA16EE04E2} ; 设备类的GUID
Provider=%ProviderName% ; 驱动的作者,由字符串域中的ProviderName指定
DriverVer=01/12/2016,21.12.36.570 ; 驱动版本
; 字符串域
[Strings]
ProviderName=”HUSTD10” ; 驱动作者
----------------------------------------------------------------------------------
版本域中涉及到新添加的设备类,同样在《竹林蹊径 深入浅出Windows驱动开发》有比较详细的描述。Provider引用了字符串域中的内容,还有其它部分也在字符串域定义了相关内容,所以这里的字符串域并不是最后的内容,后面还会定义新的内容,读者需要把这些内容组合到一起。此处需要一个设备类的GUID,如果安装了Visual Studio,那么Tools目录下有一个guidgen.exe工具,可用来生成GUID,当然也可以去网站在线生成一个GUID,如http://www.guidgen.com/。
由于我们新添加了自己的设备类HUSTD10,需要在注册表的HKLM\SYSTEM\CurrentControlSet\Control\Class下添加相关注册表项,因此,INX文件要添加的下一个域为设备类安装域:
----------------------------------------------------------------------------------
; 设备类安装域
[ClassInstall32]
Addreg=SampleClassReg ; 写注册表之类,要写入的内容放在SampleClassReg子域中
----------------------------------------------------------------------------------
----------------------------------------------------------------------------------
; SampleClassReg子域
[SampleClassReg]
HKR,,,0,%ClassName% ; 设备类的名称,由字符串域的ClassName决定
HKR,,Icon,,-5 ; 设备管理器中显示的图标,使用5号系统图标
; 字符串域
[Strings]
ClassName=”HUST Sample Device” ; 设备类的名称
----------------------------------------------------------------------------------
搞定了设备类之后,我们需要给我们的设备指定厂商、产品和设备本身的相关信息,先指定厂商域:
----------------------------------------------------------------------------------
; 厂商域
[Manufacturer]
%MfgName%=Standard,NT$ARCH$ ; 厂商名由字串串域MfgName决定,产品域有两个,
;一个为Standard,一个为Standard.NT$ARCH$(带版本信息)
; 字符串域
[Strings]
MfgName=”HUSTD10” ; 厂商名称
----------------------------------------------------------------------------------
下面是两个产品域:
----------------------------------------------------------------------------------
; Standard产品域
[Standard]
%HelloWorld.DeviceDesc%=HelloWorld_Device, root\HelloWorld ; 设备描述(可以等同理
;解为设备名)由字符串域中的HelloWorld.DeviceDesc指定,设备安装域为HelloWorld_Device,
;硬件ID为root\HelloWorld
; Standard.NT$ARCH$产品域
[Standard.NT$ARCH$]
%HelloWorld.DeviceDesc%=HelloWorld_Device, root\HelloWorld ; 同上
; 字符串域
[Strings]
HelloWorld.DeviceDesc=”HelloWorld” ; 设备描述(等同理解为设备名)
----------------------------------------------------------------------------------
写了厂商域和产品域之后,接下来就是设备本身的信息了,也就是设备域,或者设备安装域,是很重要的一个部分,也被称作DDInstall域。
----------------------------------------------------------------------------------
; 设备安装域
[HelloWorld_Device.NT]
CopyFiles=Drivers_Dir ; 需要拷贝的文件列表,由Drivers_Dir子域指定,一般这个文件就
;是我们编写的驱动文件.sys了
; Drivers_Dir子域
[Drivers_Dir]
HelloWorld.sys ; 我们编写的驱动文件
----------------------------------------------------------------------------------
有人会注意到前面产品域中定义的设备域为HelloWorld_Device,为什么后面却变成了HelloWorld_Device.NT?实际上我们不仅仅只有HelloWorld_Device.NT,还可以定义HelloWorld_Device.NTx86,HelloWorld_Device.NTIA64,或者HelloWorld_Device.NTAMD64域,它们表示不同的硬件平台版本,当然也可以不写版本,就写HelloWorld_Device,这与HelloWorld_Device.NT是相同的,表示与硬件平台无关。
下面定义设备安装域一个重要的子域——服务子域,用来向系统注册服务,为设备的运行提供支持。
----------------------------------------------------------------------------------
; 设备域服务子域
[HelloWorld_Device.NT.Services]
AddService=HelloWorld, %SPSVCINST_ASSOCSERVICE%, HelloWorld_Service_Inst ; 添加服务
;指令,添加的服务名为HelloWorld,%SPSVCINST_ASSOCSERVICE%为一个标记,指定如何增加服
;务,在Setupapi.h中定义,HelloWorld_Service_Inst为服务安装子域
; 字符串域
[Strings]
SPSVCINST_ASSOCSERVICE = 0x00000002 ; 服务安装标记
----------------------------------------------------------------------------------
下面是服务安装子域:
----------------------------------------------------------------------------------
; HelloWorld_Service_Inst服务安装子域
[HelloWorld_Service_Inst]
DisplayName = %HelloWorld.SVCDESC% ; 服务显示名称,与服务名不同
ServiceType = 1 ; 服务类型SERVICE_KERNEL_DRIVER
StartType = 3 ; 启动类型SERVICE_DEMAND_START
ErrorControl = 1 ; 错误控制级别SERVICE_ERROR_NORMAL
ServiceBinary = %12%\HelloWorld.sys ; 镜像文件路径,即.sys文件路径
; 字符串域
[Strings]
HelloWorld.SVCDESC = “Hello World Service” ; 服务显示名称
----------------------------------------------------------------------------------
接下来是设备安装域另一个重要的子域——辅助安装器子域(CoInstallers),该子域的作用是把一个或多个DLL文件记录到注册表中,并把它们拷贝到相关目录下。
----------------------------------------------------------------------------------
;设备域辅助安装器子域
[HelloWorld_Device.NT.CoInstallers]
AddReg = HelloWorld_Device_CoInstaller_AddReg ; 写注册表子域
CopyFiles = HelloWorld_Device_CoInstaller_CopyFiles ; 拷贝文件列表
; 写注册表子域
[HelloWorld_Device_CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,WdfCo-
Installer" ; 注册表项为CoInstallers32,WdfCoInstaller为入口函数
; 拷贝文件列表
[HelloWorld_Device_CoInstaller_CopyFiles]
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll ; 拷贝对应版本的.dll
----------------------------------------------------------------------------------
接着是Wdf域,安装框架的辅助安装程序之后,框架将在安装驱动程序时读取这部分。
----------------------------------------------------------------------------------
; Wdf域
[HelloWorld_Device.NT.Wdf]
KmdfService = HelloWorld, HelloWorld_wdfsect ; HelloWorld为操作系统分配给驱动程序
;的内核模式服务的名称,HelloWorld_wdfsect是一个子域
; HelloWorld_wdfsect子域
[HelloWorld_wdfsect]
KmdfLibraryVersion = $KMDFVERSION$ ; KMDF库的版本号
----------------------------------------------------------------------------------
由于INX文件中包含了文件拷贝指令CopyFiles,需要几个额外的域来指定文件位置,它们是源磁盘域([SourceDisksNames])、源文件域([SourceDisksFiles])和目的域([DestinationDirs])。
----------------------------------------------------------------------------------
; 源磁盘域
[SourceDisksNames]
1 = %DiskId1%,,,”” ; 只有一个磁盘,ID为1,磁盘描述为字符串域中的DiskId1
; 源文件域
[SourceDisksFiles]
; 本INX文件CopyFiles涉及两个文件的拷贝,HelloWorld.sys和辅助安装器WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,分别指定它们
HelloWorld.sys = 1 ; 1号磁盘
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 ; 1号磁盘
; 目的域
[DestinationDirs]
DefaultDestDir = 12 ; 默认目录,12是Windows定义的,等同于%SystemRoot%\system32\drivers
HelloWorld_Device_CoInstaller_CopyFiles = 11 ; 除了默认目录外,还有一个CopyFiles子域,
;此子域不使用默认目录,定义为11,等同于%SystemRoot%\system32。其它没有特殊定义的
;CopyFiles子域则使用默认目录
; 字符串域
[Strings]
DiskId1 = “HUSTD10 HelloWorld Disk #1” ; 1号磁盘描述
----------------------------------------------------------------------------------
好了,到此INX文件就写好了,下面给出一个完整版本的INX文件:
----------------------------------------------------------------------------------
; 完整的Hello Word驱动INX文件
; 参考WDK示例 echo.inx
;******************************************************************************
; 版本部分
;******************************************************************************
; 版本域
[Version]
Signature="$WINDOWS NT$" ; 固定
Class=HUSTSample ; 我们的驱动不属于已有的设备类,定义一个新的设备类
ClassGuid={FDA3877E-5FF3-4c18-8235-7FEA16EE04E2} ; 设备类的GUID
Provider=%ProviderName% ; 驱动的作者,由字符串域中的ProviderName指定
DriverVer=01/12/2016,21.12.36.570 ; 驱动版本
;******************************************************************************
; 设备类部分
;******************************************************************************
; 设备类安装域
[ClassInstall32]
Addreg=SampleClassReg ; 写注册表之类,要写入的内容放在SampleClassReg子域中
; SampleClassReg子域
[SampleClassReg]
HKR,,,0,%ClassName% ; 设备类的名称,由字符串域的ClassName决定
HKR,,Icon,,-5 ; 设备管理器中显示的图标,使用5号系统图标
;******************************************************************************
; 产商及设备安装部分
;******************************************************************************
; 厂商域
[Manufacturer]
%MfgName%=Standard,NT$ARCH$ ; 厂商名由字串串域MfgName决定,产品域有两个,一个
;为Standard,一个为Standard.NT$ARCH$(带版本信息)
; Standard产品域
[Standard]
%HelloWorld.DeviceDesc%=HelloWorld_Device, root\HelloWorld ; 设备描述(可以等
;同理解为设备名)由字符串域中的HelloWorld.DeviceDesc指定,设备安装域为HelloWo-
;rld_Device,硬件ID为root\HelloWorld
; Standard.NT$ARCH$产品域
[Standard.NT$ARCH$]
%HelloWorld.DeviceDesc%=HelloWorld_Device, root\HelloWorld ; 同上
; 设备安装域
[HelloWorld_Device.NT]
CopyFiles=Drivers_Dir ; 需要拷贝的文件列表,由Drivers_Dir子域指定,一般这个文件
;就是我们编写的驱动文件.sys了
; Drivers_Dir子域
[Drivers_Dir]
HelloWorld.sys ; 我们编写的驱动文件
; 设备域服务子域
[HelloWorld_Device.NT.Services]
AddService=HelloWorld, %SPSVCINST_ASSOCSERVICE%, HelloWorld_Service_Inst ; 添加
;服务指令,添加的服务名为HelloWorld,%SPSVCINST_ASSOCSERVICE%为一个标记,指定如何
;增加服务,在Setupapi.h中定义,HelloWorld_Service_Inst为服务安装子域
; HelloWorld_Service_Inst服务安装子域
[HelloWorld_Service_Inst]
DisplayName = %HelloWorld.SVCDESC% ; 服务显示名称,与服务名不同
ServiceType = 1 ; 服务类型SERVICE_KERNEL_DRIVER
StartType = 3 ; 启动类型SERVICE_DEMAND_START
ErrorControl = 1 ; 错误控制级别SERVICE_ERROR_NORMAL
ServiceBinary = %12%\HelloWorld.sys ; 镜像文件路径,即.sys文件路径
;******************************************************************************
; 辅助安装器部分
;******************************************************************************
;设备域辅助安装器子域
[HelloWorld_Device.NT.CoInstallers]
AddReg = HelloWorld_Device_CoInstaller_AddReg ; 写注册表子域
CopyFiles = HelloWorld_Device_CoInstaller_CopyFiles ; 拷贝文件列表
; 写注册表子域
[HelloWorld_Device_CoInstaller_AddReg]
HKR,,CoInstallers32,0x00010000,"WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,
WdfCoInstalle-r" ; 注册表项为CoInstallers32,WdfCoInstaller为入口函数
; 拷贝文件列表
[HelloWorld_Device_CoInstaller_CopyFiles]
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll ; 拷贝对应版本的.dll
; Wdf域
[HelloWorld_Device.NT.Wdf]
KmdfService = HelloWorld, HelloWorld_wdfsect ; HelloWorld为操作系统分配给驱动
;程序的内核模式服务的名称,HelloWorld_wdfsect是一个子域
; HelloWorld_wdfsect子域
[HelloWorld_wdfsect]
KmdfLibraryVersion = $KMDFVERSION$ ; KMDF库的版本号
;******************************************************************************
; 文件域部分
;******************************************************************************
; 源磁盘域
[SourceDisksNames]
1 = %DiskId1%,,,”” ; 只有一个磁盘,ID为1,磁盘描述为字符串域中的DiskId1
; 源文件域
[SourceDisksFiles]
; 本INX文件CopyFiles涉及两个文件的拷贝,HelloWorld.sys和辅助安装器WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll,分别指定它们
HelloWorld.sys = 1 ; 1号磁盘
WdfCoInstaller$KMDFCOINSTALLERVERSION$.dll = 1 ; 1号磁盘
; 目的域
[DestinationDirs]
DefaultDestDir = 12 ; 默认目录,12是Windows定义的,等同于%SystemRoot%\system32\
;drivers
HelloWorld_Device_CoInstaller_CopyFiles = 11 ; 除了默认目录外,还有一个Copy-
;Files子域,此子域不使用默认目录,定义为11,等同于%SystemRoot%\system32。其它没
;有特殊定义的CopyFiles子域则使用默认目录
;******************************************************************************
; 字符串部分
;******************************************************************************
; 字符串域
[Strings]
ProviderName="HUSTD10" ; 驱动作者
ClassName="HUST Sample Device" ; 设备类的名称
MfgName="HUSTD10" ; 厂商名称
HelloWorld.DeviceDesc="HelloWorld" ; 设备描述(等同理解为设备名)
SPSVCINST_ASSOCSERVICE = 0x00000002 ; 服务安装标记
HelloWorld.SVCDESC = "Hello World Service" ; 服务显示名称
DiskId1 = "HUSTD10 HelloWorld Disk #1" ; 1号磁盘描述
----------------------------------------------------------------------------------
该INX文件相对于最终的INF文件,多了表示版本的几个字符串变量,它们分别是:ARCH,KMDFCOINSTALLERVERSION和KMDFVERSION。ARCH表示硬件平台,使用x86编译环境,ARCH 会被替换为“x86”,KMDFCOINSTALLERVERSION表示辅助安装器(Co-installer)的版本,KMDFVERSION表示KMDF库的版本。它们都会在生成INF文件时被替换,读者可以自行比较。
INX文件生成INF文件是通过WDK中bin目录下的StampInf.exe工具完成的,后面我们通过批处理命令在makefile中调用它。现在,我们先手动运行它来看看。进入WDK编译环境,切换到HelloWorld.inx目录下,输入stampinf -?查看帮助。
Stampinf直接修改文件,因此我们把HelloWorld.inx复制一份,命名为HelloWorld.inf,输入命令: stampinf -f HelloWorld.inf -a x86 -k 1.9,命令表示x86硬件平台,KMDF主版本号为1,次版本号为9。Stampinf还可以给DriverVer打上时间戳。
可以使用另一个工具来检查生成的inf文件的语法,这个工具为ChkInf,位于tools\Chkinf目录下,是一个脚本。切换到tools\Chkinf目录下,输入:chkinf D:\HUSTD10\HelloWorld.inf /O D:\Temp 命令,chkinf会将结果放到d:\temp目录下,用浏览器查看即可。
好了,Inf文件就说这么多,接下来还有一个文件要编写,即sources文件,这个文件比较简单,就不一步一步写了。
----------------------------------------------------------------------------------
TARGETNAME=HelloWorld # 编译后.sys文件名
TARGETTYPE=DRIVER # 编译类型为驱动
KMDF_VERSION_MAJOR=1 # KMDF主版本
INF_NAME=HelloWorld # 通过 INX生成的INF文件名,这个变量在makefile.inc中被引用
INCLUDES=$(INCLUDES);..\..\inc #头文件目录
NTTARGETFILE0=$(OBJ_PATH)\$(O)\$(INF_NAME).inf
PASS0_BINPLACE=$(NTTARGETFILE0) #通过INX生成的INF文件路径
C_DEFINES= $(C_DEFINES) #一些编译器的开关
SOURCES=driver.c # 源代码文件列表
----------------------------------------------------------------------------------
现在,编译所需的文件就都准备好了,打开编译环境,进入项目目录,输入build命令编译,编译生成了HelloWorld.sys,HelloWorld.inf以及用于调试的符号文件。下面我们来安装驱动。
6 安装Hello World驱动
由于HelloWorld驱动为纯软件驱动,所以驱动不能像我们插上U盘那样自动安装,因为即插即用管理器检测不到新的设备。而且驱动程序没有测试签名,只能用管理员身份,以批准驱动的安装。
好文章,来自【福优学苑@音视频+流媒体】
***【在线视频教程】***