从示例代码到生产驱动程序 - WDK 示例中的具体更改
本主题介绍了在根据示例代码发布设备驱动程序之前,需要对 WDK 示例驱动程序进行的重要更改。
除了此处所述的更改,所有驱动程序还应采用创建可靠的内核模式驱动程序和 Surface 团队驱动程序开发最佳做法中所述的最佳做法。 所有驱动程序还应遵循驱动程序安全指南中提供的准则。
WDK 驱动程序示例 - 唯一标识符
Windows 驱动程序工具包 (WDK) 包含各种示例驱动程序,它们演示了用于驱动程序开发的有用技术。 你可使用这些示例作为你自己的驱动程序的基础,但在发布驱动程序之前,除了明显的操作代码外,还必须更改示例中某些特定于设备的方面,才能唯一地应用于你自己的设备和驱动程序。 驱动程序编写人员有时会忽略这些详细信息。
必须更改的确切项目因示例而异,但是在通常情况下,它们会标识特定设备、接口或驱动程序。 例如,如果示例驱动程序包含以下任何项,必须将其更改为适用于你的驱动程序和设备:
全局唯一标识符 (GUID)
符号链接名称
设备对象名称
池标记
I/O 控制代码 (IOCTL) 定义
复制到系统文件夹的任何文件的名称
即插即用设备 ID、硬件 ID 和兼容 ID
驱动程序服务名称
设备说明
资源文件
如果忘记进行这些更改,可能会导致安装失败,与系统上的其他设备和驱动程序冲突,调试困难,还可能导致其他错误。
例如,如果收到 ...\toastDrv\kmdf\toastmon\wdftoastmon.inx(18-18): error 1284: Class "Sample" is reserved for use by Microsoft.
之类的错误,则表示必须将“Sample”名称更改为示例驱动程序的唯一名称。
GUID
驱动程序使用 GUID 来标识设备安装程序类、设备接口类、自定义 PnP 事件、自定义 Windows Management Instrumentation (WMI) 事件和 Windows 预处理器 (WPP) 跟踪提供程序。 一些 GUID 由 Microsoft 定义,其他 GUID 由设备和驱动程序供应商定义。
用于常见设备和 WMI 数据的设备安装程序类 GUID、设备接口类 GUID 和 WMI GUID 是在 WDK 或公共头文件中定义的,可供任何驱动程序使用。 不应更改这些 GUID。
例如,如果要实现鼠标,可继续使用在 WDK Ntddmou.h 头文件中定义的 GUID_DEVINTERFACE_MOUSE 作为设备接口类。但是,如果定义新的设备安装程序类,则必须生成新的设备安装程序类 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 或 Guidgen.exe 中的“创建 GUID”工具,这二者都包含在 Microsoft Windows 软件开发工具包 (SDK) 中。 然后,可将 GUID 与驱动程序头文件中的符号常数相关联,如示例所示。
你可能还需要为驱动程序的 WMI 事件创建新的 GUID。 Toaster 驱动程序示例定义以下 GUID,来获取 Toaster 设备到达通知:
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。 例如,%WinDDK%\Src\Kmdf\Osrusbfx2\Final 中的 Osrusbfx2 示例的 Trace.h 头文件定义控件 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 驱动程序创建一个符号链接名称,它使用在 Filter.h 头文件中如下定义的字符串:
#define SYMBOLIC_NAME_STRING L"\\DosDevices\\ToasterFilter"
更改粗体字符串来更准确地描述自己的驱动程序。
设备对象名称
如果示例创建了设备对象的名称,则在修改示例代码时必须更改该名称。
KMDF Toaster Filter 驱动程序在 Filter.h 头文件中为其设备对象命名,如下所示:
#define NTDEVICE_NAME_STRING L\\Device\\ToasterFilter
与符号链接名称一样,你应更改字符串来描述你的驱动程序。
请记住,命名设备对象可能会带来安全风险。 物理设备对象 (PDO) 必须具有名称,并且大多数此类名称是系统生成的,而不是由驱动程序显式分配。 仅当其他设备对象代表控制设备对象(用于应用程序和驱动程序之间的边带通信)时,才应命名这些对象。 通过内核模式驱动程序框架 (KMDF) 和 Windows 驱动程序模型 (WDM),可要求 Windows 生成名称。 此方法可确保设备对象的名称是唯一的,并且没有特权的用户无法访问它。 有关详细信息,请参阅控制设备命名空间访问权限和控制 KMDF 驱动程序中的设备访问权限。
池标记
池标记是一个由 4 个字符组成的文本,用于标识特定内存分配,有助于进行调试。
许多示例驱动程序在驱动程序头文件中定义了一个池标记,如 Toaster.h 中的以下行所示:
#define TOASTER_POOL_TAG (ULONG) 'saoT'
由于调试程序以相反顺序显示标记,因此驱动程序将向后定义标记。 因此,此标记在调试程序输出中显示为 Toas。 不要使用此示例定义的标记,而应更改字符串来唯一标识自己的代码。
Pooltag.txt 文件列出了 Windows 提供的内核模式组件和驱动程序所使用的池标记。 Pooltag.txt 与 WDK 一并安装在 %winddk%\Tools\Other<i>platform\Poolmon 中,其中 platform 是 amd64、i386 或 ia64。 不要使用此列表中出现的任何标记。
IOCTL 定义
更改任何示例定义的 I/O 控制代码来使用适用于你的设备和驱动程序的名称、设备类型、功能代码、传输类型和访问类型。
例如,对于 IOCTL_OSRUSBFX2_READ_SWITCHES,Osrusbfx2 示例包括以下定义:
#define IOCTL_OSRUSBFX2_READ_SWITCHES CTL_CODE(FILE_DEVICE_OSRUSBFX2, \ IOCTL_INDEX + 6, \ METHOD_BUFFERED, \ FILE_READ_ACCESS)
适用于不同设备的基于示例的驱动程序需要修改此定义。
文件名
在 INF 或 INX 中,更改驱动程序的名称、供应商提供的辅助安装程序,以及安装过程复制到系统文件夹的任何其他文件。 这些文件名通常出现在 INF 的 [SourceDisksFiles] and [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 辅助安装程序。
稍后需要在文件的“设备安装”部分进行其他更改,如以下示例中所示:
[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 文件进行匹配。 设备 ID 在 INF 的 [Manufacturer] 部分定义。
以下示例显示了 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 是安装程序使用的不太具体的 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 代码。
更新复制到系统文件夹的任何文件的名称。
在 INF 中插入正确的即插即用设备 ID、硬件 ID 和兼容 ID。
在 INF 中更新驱动程序的服务名称。
更改设备说明。
修改资源文件中任何特定于驱动程序的字符串。
遵循有关安全性和可靠性的最佳做法
其他信息
书籍
《使用 Windows Driver Foundation 开发驱动程序》(Developing Drivers with the Windows Driver Foundation),作者:Penny Orwick 和 Guy Smith