Windows Driver Kit (WDK) 包含大量样例驱动程序,这些样例演示了有用的驱动程序开发技术。您可以使用这些样例作为您自己的驱动程序的基础,但是在发布驱动程序之前,您必须更改样例的某些设备特定的方面,以适合于您自己的设备和驱动程序。驱动程序编写人员有时会忽略这些细节。
对于您必须更改的确切项,每个示例都有所不同。但是总体来说,它们标识一个特定的设备、接口或驱动程序。例如,如果示例驱动程序包含下列项中的任意一个,那么您必须更改它们以适合于您的驱动程序和设备:
| • | 全局唯一标识符 (GUID) |
| • | 符号链接名 |
| • | 设备对象名 |
| • | 池标记 |
| • | I/O 控制码 (IOCTL) 定义 |
| • | 用户模式驱动程序框架 (UMDF) 驱动程序对象类 ID |
| • | 复制到系统文件夹的任何文件的名称 |
| • | 即插即用设备 ID、硬件 ID 和兼容 ID |
| • | 驱动程序服务名 |
| • | 设备描述 |
| • | 资源文件 |
忘记进行这些更改会导致安装失败、与系统中的其他设备和驱动程序冲突、调试困难以及其他错误。
GUID。驱动程序使用 GUID 来标识设备安装程序类、设备接口类、自定义的 PnP 事件、自定义的 Windows 管理规范 (WMI) 事件和 Windows 预处理器 (WPP) 跟踪提供程序。一些 GUID 由 Microsoft 定义,其他 GUID 由设备和驱动程序供应商定义。
常用设备和 WMI 数据的设备安装程序类 GUID、设备接口类 GUID 和 WMI GUID 在 WDK 或公共头文件中定义,可供任何驱动程序使用。您不应该更改这些 GUID。例如,如果您正在实现一个鼠标,那么您将继续使用 GUID_DEVINTERFACE_MOUSE(在 WDK Ntddmou.h 头文件中定义)作为设备接口类。
但是,如果您定义一个新的设备安装程序类,那么您必须生成一个新的设备安装程序类 GUID 和安装类名称,并且还可能必须生成一个新的设备接口类 GUID。安装程序类 GUID 和设备接口类 GUID 必须是唯一的值;它们不能共享一个 GUID。
对于大部分基于样例的驱动程序,您应该只更改在样例的局部头文件或源文件中定义的 GUID,从而使其特定于该样例。这类 GUID 可以包括:
| • | 自定义 PnP 事件 |
| • | 自定义 WMI 事件 |
| • | 新设备或自定义设备的设备接口类 |
| • | WPP 跟踪提供程序 |
如果两个驱动程序都被加载到相同的系统上,那么使用已经为另一个驱动程序定义的 GUID 会导致冲突。例如,如果两个不同的驱动程序使用相同的 GUID 来注册设备接口,那么试图打开设备接口的客户可能会无意中打开错误的设备。
下面的摘录来自包含在所有 Toaster 驱动程序样例中的 Driver.h 文件。它为 Toaster 设备定义设备接口 GUID:
DEFINE_GUID(GUID_TOASTER_INTERFACE_STANDARD, \ 0xe0b27630, 0x5434, 0x11d3, 0xb8, 0x90, 0x0, 0xc0, \ 0x4f, 0xad, 0x51, 0x71); // {E0B27630-5434-11d3-B890-00C04FAD5171}
如果您在自己的驱动程序中使用这个文件,那么请确保您使用自己的设备接口 GUID 来替换样例 GUID(上面以粗体文本显示的内容)。要创建一个 GUID,请使用 Microsoft Visual Studio 中的 Create GUID 工具或使用 Uuidgen.exe 或 Guidgen.exe(这两个工具都包含在 Microsoft Windows Software Development Kit (SDK) 中)。然后您可以将 GUID 与驱动程序头文件中的符号常量关联,如例子所示。
您可能还需要为驱动程序的 WMI 事件创建新的 GUID。Toaster 驱动程序样例为 toaster 设备到达的通知定义下列 GUID:
DEFINE_GUID (TOASTER_NOTIFY_DEVICE_ARRIVAL_EVENT, \ 0x1cdaff1, 0xc901, 0x45b4, 0xb3, 0x59, 0xb5, 0x54, \ 0x27, 0x25, 0xe2, 0x9c); // {01CDAFF1-C901-45b4-B359-B5542725E29C}
您应该为您的驱动程序中的每个 WMI 事件创建一个新的 GUID。
如果样例驱动程序使用 WPP 软件跟踪,那么为基于样例的任何驱动程序生成一个新的跟踪提供程序 GUID。例如,Osrusbfx2 样例的 Trace.h 头文件(在 %WinDDK%\Src\Kmdf\Osrusbfx2\Final 中)按如下方式定义一个控件 GUID:
#define WPP_CONTROL_GUIDS \ WPP_DEFINE_CONTROL_GUID( \ OsrUsbFxTraceGuid,(d23a0c5a,d307,4f0e,ae8e,E2A355AD5DAB), \ WPP_DEFINE_BIT(DBG_INIT) /* bit 0 = 0x00000001 */ \ WPP_DEFINE_BIT(DBG_PNP) /* bit 1 = 0x00000002 */ \ WPP_DEFINE_BIT(DBG_POWER) /* bit 2 = 0x00000004 */ \ WPP_DEFINE_BIT(DBG_WMI) /* bit 3 = 0x00000008 */ \ WPP_DEFINE_BIT(DBG_CREATE_CLOSE) /* bit 4 = 0x00000010 */ \ WPP_DEFINE_BIT(DBG_IOCTL) /* bit 5 = 0x00000020 */ \ WPP_DEFINE_BIT(DBG_WRITE) /* bit 6 = 0x00000040 */ \ WPP_DEFINE_BIT(DBG_READ) /* bit 7 = 0x00000080 */ \ )
在您自己的驱动程序中,您将使用驱动程序特定的名称和创建的 GUID 来替换粗体文本。
符号链接名。如果样例定义一个符号链接名,那么使用适合于您自己的驱动程序的名称来替换样例中的名称。但是,不要改变众所周知的链接名(例如 \DosDevices\COM1)。总体来讲,如果链接名与样例名十分相似(例如 \DosDevices\CancelSamp),那么您应该更改它。使用与另一个驱动程序相同的符号链接与使用错误的设备接口 GUID 具有相同的效果,因为设备接口本质上是符号链接。
%WinDDK\Src\Kmdf\Toaster\Filter 中的 KMDF Toaster 筛选器驱动程序创建一个符号链接名,该链接名使用 Filter.h 头文件中按如下方式定义的字符串:
#define SYMBOLIC_NAME_STRING L"\\DosDevices\\ToasterFilter"
更改粗体字符串以更精确地描述您自己的驱动程序。
设备对象名。如果样例为设备对象创建一个名称,那么您必须在改编样例代码时更改名称。
KMDF Toaster 筛选器驱动程序在 Filter.h 头文件中按如下方式命名其设备对象:
#define NTDEVICE_NAME_STRING L\\Device\\ToasterFilter
至于符号链接名,您应该更改该字符串来描述您的驱动程序。
请记住,命名的设备对象可能预示着安全风险。物理设备对象 (PDO) 必须拥有名称,大部分这种名称都是系统生成的,而不是由驱动程序显式指定的。其他设备对象只有在它们代表控制设备对象(用于应用程序和驱动程序之间的边带通信)时才应该被命名。内核模式驱动程序框架 (KMD) 和 Windows Driver Model (WDM) 都允许您让 Windows 生成名称。这种方法可以确保设备对象名称的唯一性并且使得无特权的用户不能访问它。详细信息请参见 WDK。
池标签。池标签是一个一到四个字符的文本(标识特定的内存分配)并且可以帮助调试。
许多样例驱动程序在驱动程序头文件中定义池标签,如同 Toaster.h 中的下列行所示:
#define TOASTER_POOL_TAG (ULONG) 'saoT'
驱动程序按相反方向定义标签,因为调试器按照相反的顺序显示它。这样,这个标签在调试器输出中显示为 'Toas'。不要使用样例定义的标签,而应该更该改字符串来唯一地标识您自己的代码。
Pooltag.txt 文件列出 Windows 提供的内核模式组件和驱动程序使用的池标签。Pooltag.txt 随 WDK 一起安装在 %winddk%\Tools\Other\platform\Poolmon 中,其中 platform 是 amd64、i386 或 ia64。不要使用该列表中显示的任何标签。
IOCTL 定义。更改样例定义的任何 I/O 控制码来使用适用于您的设备和驱动程序的名称、设备类型、函数代码、传输类型和访问类型。
例如,Osrusbfx2 样例包含 IOCTL_OSRUSBFX2_READ_SWITCHES 的下列定义:
#define IOCTL_OSRUSBFX2_READ_SWITCHES CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ IOCTL_INDEX + 6, \ METHOD_BUFFERED, \ FILE_READ_ACCESS)
不同设备的基于样例的驱动程序将需要修改这个定义。
UMDF 驱动程序对象类 ID。如果您使用一个 UMDF 样例,那么您必须更改设备对象的类 ID。这个值通常在 Internal.h 头文件中定义为 MYDRIVER_CLASS_ID,但是一些样例在不同的头文件中定义它。
下面的样例来自 Skeleton 驱动程序的 Internal.h 头文件。您可以在 %WinDDK\Src\Umdf\Skeleton 中找到这个文件。
#define MYDRIVER_CLASS_ID { 0xd4112073, 0xd09b, 0x458f, \ { 0xa5, 0xaa, 0x35, 0xef, 0x21, 0xee, 0xf5, 0xde } }
如果您的驱动程序基于 Skeleton 样例,那么您需要把上面粗体显示的定义更改为您的驱动程序对象的唯一类 ID。
文件名。在 INF 或 INX 中,更改驱动程序的名称、供应商提供的共同安装程序以及安装过程复制到系统文件夹的任何其他文件。这些文件名通常显示在 INF 文件的 [SourceDisksFiles] 和 [ClassInstall32] 节中以及 CopyFiles 条目中。
下面的样例来自 KMDF 中 Featured Toaster 例子的 INX 文件,它可以在 %WinDDK%\src\kmdf\Toaster\Func\Featured 中找到。必须更改的文件名以粗体显示:
[ClassInstall32] Addreg=ToasterClassReg CopyFiles=ToasterClassInstallerCopyFileshighlight [ToasterClassReg] ...HKR,,Installer32,,"tostrcls.dll,ToasterClassInstaller" ... [ToasterClassInstallerCopyFiles] tostrcls.dll ...
要针对不同驱动程序修改文件的这个部分,您需要把 "tostrcls.dll" 更改为您的类安装程序的文件名并更改 "ToasterClassInstaller" 字符串来描述您自己的安装程序。这些更改可以确保安装过程复制正确的共同安装程序文件,并且注册表项记录正确的文件名。
不要更改 WDK 或 Windows 中提供的共同安装程序的名称,例如 KMDF、UMDF 和 WinUSB 共同安装程序。
以后需要在文件的 Device Install 节中进行其他更改如本样例中所示:
[Toaster_Device.NT] CopyFiles=Toaster_Device.NT.Copy [Toaster_Device.NT.Copy] wdffeatured.sys
在这个样例中,您需要把粗体的文件名更改为生成的驱动程序文件的名称。
当安装程序复制 INF 和驱动程序目录文件时,它对它们进行重命名,因此并不是一定需要更改它们在驱动程序包中的名称。但是,确保 INF 和目录文件名与驱动程序文件名相似通常是一个好主意。
PnP 设备 ID、硬件 ID 和兼容 ID。安装程序使用设备 ID 以及硬件 ID 和兼容 ID 来选择用于设备安装的 INF。
设备 ID 是唯一标识特定设备的供应商定义字符串。每个设备都有一个设备 ID。在枚举期间总线驱动程序报告设备 ID,安装程序用它来将设备与正确的 INF 文件匹配。在 INF 文件的 [Manufacturer] 节中定义设备 ID。
下面的例子显示 OSR USB Fx2 设备的设备 ID(如同 Osrusbfx2.inx 文件中指定的那样):
[Manufacturer] %MfgName%=Microsoft,NT$ARCH$ ; For Win2K [Microsoft] %USB\VID_045E&PID_930A.DeviceDesc%=osrusbfx2.Dev, USB\VID_0547&PID_1002 ... ; For XP and later [Microsoft.NT$ARCH$] %USB\VID_045E&PID_930A.DeviceDesc%=osrusbfx2.Dev, USB\VID_0547&PID_1002
要针对您自己的驱动程序更改这个 INF 指令,请使用您自己的设备的设备 ID 来替代粗体显示的设备 ID。您还应该将厂商名更改为您公司的名称。
如果不能将设备 ID 与 INF 匹配,那么安装程序使用的硬件 ID 和兼容 ID 的特定程度就比较小。如果您的 INF 支持其他设备,那么您应该更改这些值(除了设备 ID)。下面这个来自 KMDF 的 Featured Toaster 驱动程序的样例显示了硬件 ID:
[Manufacturer] %StdMfg%=Standard,NT$ARCH$
; For Win2K [Standard] ; DisplayName Section DeviceId ; ----------- ------- -------- %ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster
; For XP and later [Standard.NT$ARCH$] %ToasterDevice.DeviceDesc%=Toaster_Device, {b85b7c50-6a01-11d2-b841-00c04fad5171}\MsToaster
要针对您自己的驱动程序更改这个 INF 指令,请使用您的驱动程序的设备 ID 来替换硬件 ID 并将 "MsToaster" 更改成更具描述性的字符串。
驱动程序服务名。将 INF 中 AddService 指令中的服务名更新为适合于您的驱动程序的值。如果驱动程序服务名与系统上另一个驱动程序的服务名冲突,那么驱动程序不会安装或加载。
KMDF Featured Toaster 驱动程序将其服务命名如下:
[Toaster_Device.NT.Services] AddService = wdffeatured, %SPSVCINST_ASSOCSERVICE%, wdffeatured_Service_Inst
服务名是 AddService 指令中的第一个条目。要修改 Featured Toaster 的 INF,您应该将粗体字符串更改为更适合于您的驱动程序的字符串。在样例中,wdffeatured_Service_Inst 条目只引用了一个 INF 定义的节,因此对它的更改无关紧要。
设备描述。设备描述由几个字符串(通常在 INF 的 [Strings] 节中定义并在 INF 的不同地方使用)组成。例如,KMDF Featured Toaster 样例在 WdfFeatured.inx 文件中定义下列字符串:
[Strings] SPSVCINST_ASSOCSERVICE = 0x00000002 MSFT = "Microsoft" StdMfg = "(Standard system devices)" ClassName = "Toaster" DiskId1 = "Toaster Device Installation Disk #1" ToasterDevice.DeviceDesc = "Microsoft WDF Featured Toaster" Toaster.SVCDESC = "Microsoft WDF Toaster Featured Device Driver"
要修改这个文件来安装您自己的驱动程序,您应该更改粗体显示的字符串来反应关于您的公司、设备和驱动程序的信息。
如果公司名也显示在 INF 的 [Manufacturer] 节中,那么您还必须更改那里的名称。
资源文件。像样例特定的共同安装程序这样的驱动程序和其他组件也有资源 (.rc) 文件,这种文件定义驱动程序特定的字符串,包括产品名称、文件版本和公司名称。将这些字符串更改为适合于您的驱动程序包的合适的值。
发布一个基于 WDK 样例的驱动程序之前,替换源文件、INF 和您用来创建自己的驱动程序的任何其他资源中的任何样例特定的信息。每个样例所需的改变都有所不同,但是通常都包含唯一标识样例驱动程序或其设备的所有信息。下面是您必须进行的典型更改:
| • | 在合适的地方生成和使用特定于您的驱动程序的 GUID。 |
| • | 更新符号链接名。 |
| • | 更新设备对象的名称或使用一个自动生成的名称。 |
| • | 使用标识您的驱动程序并且不与任何已知标签冲突的池标签。 |
| • | 定义适合于您的驱动程序和设备的 IOCTL 代码。 |
| • | 为 UMDF 驱动程序对象使用一个唯一的类 ID。 |
| • | 更新复制到系统文件夹的任何文件的名称。 |
| • | 在 INF 中插入正确的即插即用设备 ID、硬件 ID 和兼容 ID。 |
| • | 更新 INF 中驱动程序的服务名。 |
| • | 更改设备描述。 |
| • | 在资源文件中修改任何驱动程序特定的字符串。 |
图书
Developing Drivers with the Windows Driver Foundation,由 Penny Orwick 和 Guy Smith 编写
WHDC 网站上的驱动程序技巧
谁在使用池?
WDK 文档
定义和导出新的 GUID
在基于框架的驱动程序中控制设备访问
IoCreateDeviceSecure
Windows SDK
Microsoft Windows Software Development Kit (SDK)