从驱动程序存储区运行

使用“从驱动程序存储区运行”的 INF 意味着该 INF 使用 DIRID 13 指定安装时驱动程序包文件的位置。

对于由 INF 负载的“从驱动程序存储区运行”文件,INF 中该文件的 SourceDisksFiles 条目中列出的 subdir 必须与 INF 中该文件的 DestinationDirs 条目中列出的 subdir 匹配

此外,CopyFiles 指令不能用于重命名从驱动程序存储区运行的文件。 这些限制是必需的,以便在设备上安装 INF 时不会在驱动程序存储目录中创建新文件。

由于 SourceDisksFiles 条目不能包含多个具有相同文件名的条目,并且 CopyFiles 不能用于重命名文件,因此 INF 引用的每个“从驱动程序存储区运行”文件都必须具有唯一的文件名。

从 Windows 10 1709 开始,驱动程序包通常支持“从驱动程序存储区运行”。 不过,某些设备堆栈可能会对需要提供的插入该堆栈的文件设置额外的限制。 例如,这些设备堆栈在 Windows 10 1803 之前不支持“从驱动程序商店运行”:

如果提供的二进制文件要插入特定的设备栈,请查阅要插入的特定设备堆栈的文档,以检查它是否支持提供二进制文件的完整文件路径,以及对完整文件路径是否有任何限制。 如果它支持提供到二进制文件的完整文件路径,且对该路径没有限制,则它应支持文件“从驱动程序存储区运行”。

从驱动程序存储区动态查找和加载文件

有时,组件需要加载一个文件,该文件是使用“从驱动程序存储区运行”的驱动程序包的一部分。 这些驱动程序包文件的路径不应硬编码,因为不同版本的驱动程序包、不同操作系统版本、不同操作系统版本等都可能有所不同。在需要加载驱动程序包文件时,应使用下面描述的一些范例来发现并动态加载这些驱动程序包文件。

查找并加载同一驱动程序包中的文件

当驱动程序包中的文件需要从同一驱动程序包加载另一个文件时,动态发现该文件的一个可用选项是确定运行该文件的目录并相对于该目录加载另一个文件。

从 Windows 10 版本 1803 及更高版本上的驱动程序存储区运行的 WDM 或 KMDF 驱动程序需要访问其驱动程序包中的其他文件时,应使用 DriverDirectoryImage 作为目录类型来调用 IoGetDriverDirectory,以获取从中加载驱动程序的目录路径。 或者,对于需要支持 Windows 10 版本 1803 之前的 OS 版本的驱动程序,使用 IoQueryFullDriverPath 查找驱动程序的路径,获取用于加载驱动程序的目录路径,并查找相对于该路径的文件。 如果内核模式驱动程序是 KMDF 驱动程序,则它可以使用 WdfDriverWdmGetDriverObject 来检索要传递到 IoQueryFullDriverPath 的 WDM 驱动程序对象。

用户模式二进制文件可以使用 GetModuleHandleExWGetModuleFileNameW 来确定二进制文件的加载位置。 例如,UMDF 驱动程序二进制文件可能会执行以下操作:

bRet = GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                         (PCWSTR)&DriverEntry,
                         &handleModule);
if (bRet) {
    charsWritten = GetModuleFileNameW(handleModule,
                                      path,
                                      pathLength);
    …

查找并加载任意驱动程序包中的文件

在某些情况下,驱动程序包可能包含应该由另一驱动程序包中的二进制文件加载或由某个用户模式组件加载的文件。 如果此方法比上述从同一驱动程序包加载文件的方法更可取,则此方法也可用于来自同一驱动程序包的文件。

以下是可能涉及从驱动程序包加载文件的几个方案示例:

  • 驱动程序包中的用户模式 DLL 提供一个用来与驱动程序包中的驱动程序通信的接口。

  • 扩展驱动程序包包含一个由基准驱动程序包中的驱动程序加载的配置文件。

在这些情况下,驱动程序包应在设备或设备接口上设置某种状态,以指示预期要加载的文件的路径。

驱动程序包通常会使用 HKR AddReg 来设置此状态。 在此示例中,应该假定:对于 ExampleFile.dll,驱动程序包有一个没有任何 subdirSourceDisksFiles 条目。 这样就会将文件置于驱动程序包目录的根目录中。 还应假设 CopyFiles 指令的 DestinationDirs 指定 dirid 13

下面是将其设置为设备状态的 INF 示例:

[ExampleDDInstall.HW]
AddReg = Example_DDInstall.AddReg

[Example_DDInstall.AddReg]
HKR,,ExampleValue,,%13%\ExampleFile.dll

而将其设置为设备接口状态的 INF 示例为:

[ExampleDDInstall.Interfaces]
AddInterface = {<fill in an interface class GUID for an interface exposed by the device>},,Example_Add_Interface_Section

[Example_Add_Interface_Section]
AddReg = Example_Add_Interface_Section.AddReg

[Example_Add_Interface_Section.AddReg]
HKR,,ExampleValue,,%13%\ExampleFile.dll

前面的示例使用空的标志值,这会导致生成 REG_SZ 注册表值。 这样就会将 %13% 转换成完全限定的用户模式文件路径。 在许多情况下,最好将路径设为相对于环境变量。 如果使用标志值 0x20000,则注册表值为类型 REG_EXPAND_SZ,而 %13% 则会转换为一个包含相应环境变量的路径,该变量用于抽象路径的位置。 检索此注册表值时,请调用 ExpandEnvironmentStrings 来解析路径中的环境变量。

如果此值需由内核模式组件读取,则此值应该是 REG_SZ 值。 内核模式组件在读取该值时应该在其前面预置 \??\,然后再将其传递给 ZwOpenFile 之类的 API。

要在它是设备状态的一部分时访问此设置,应用程序必须首先找到设备的标识。 用户模式代码可以使用 CM_Get_Device_ID_List_SizeCM_Get_Device_ID_List 获取设备的列表(按需筛选)。 该设备列表可能包含多个设备,因此请先搜索相应的设备,然后再从设备读取状态。 例如,在查找符合特定条件的设备时,请调用 CM_Get_DevNode_Property 来检索设备的属性。

找到正确的设备以后,请调用 CM_Open_DevNode_Key 来获取存储设备状态的注册表位置的句柄。

内核模式代码应检索具有状态的设备的 PDO(物理设备对象)并调用 IoOpenDeviceRegistryKey。 内核模式代码检索设备的 PDO 的一种可能方法是发现设备公开的已启用接口,并使用 IoGetDeviceObjectPointer 检索设备对象

要在设备接口状态下访问此设置,用户模式代码可以调用 CM_Get_Device_Interface_List_SizeCM_Get_Device_Interface_List

另外,可以使用 CM_Register_Notification 来获取设备接口的到达和删除通知,这样就可以在启用接口时通知代码,让代码随后检索状态。 在设备接口类(在上述 API 中使用)中可能有多个设备接口。 检查这些接口,确定哪个接口是适合读取设置的接口。

找到正确的设备接口以后,调用 CM_Open_Device_Interface_Key

内核模式代码可以检索从其获取状态的设备接口的符号链接名称。 为此,请调用 IoRegisterPlugPlayNotification,以便注册获取相应设备接口类的设备接口通知。 也可调用 IoGetDeviceInterfaces,获取系统中当前设备接口的列表。 在设备接口类(在上述 API 中使用)中可能有多个设备接口。 检查这些接口,确定哪个接口是应该读取设置的正确接口。

找到适当的符号链接名称以后,请调用 IoOpenDeviceInterfaceRegistryKey,以便检索在其中存储了设备接口状态的注册表位置的句柄。

注意

CM_GETIDLIST_FILTER_PRESENT 标志与 CM_Get_Device_ID_List_SizeCM_Get_Device_ID_List 配合使用,或者将 CM_GET_DEVICE_INTERFACE_LIST_PRESENT 标志与 CM_Get_Device_Interface_List_SizeCM_Get_Device_Interface_List 配合使用。 这可确保与包含文件路径的状态相关的硬件存在并已做好通信准备。

删除驱动程序包

默认情况下,如果驱动程序包仍安装在任何设备上,则无法从系统中删除。 但是,一些用于从系统中删除驱动程序包的选项允许尝试将其“强制”删除。 即使驱动程序包仍安装在系统的某些设备上,此操作也将尝试删除该驱动程序包。 对于包含任何“从驱动程序存储区运行”的文件的驱动程序包,不允许强制删除。 从系统中删除驱动程序包时,会删除其驱动程序存储区内容。 如果仍有任何设备随该驱动程序包一起安装,则该驱动程序包中的任何“从驱动程序存储区运行”文件都将消失,这些丢失的文件可能会导致该设备出现故障。 为了防止设备进入类似的不良状态,无法强制删除包含任何“从驱动程序存储区运行”文件的驱动程序包。 只有当它们不再安装在任何设备上后,才能将其删除。 为帮助删除此类驱动程序包,可以使用 DiUninstallDriverpnputil /delete-driver <oem#.inf> /uninstall。 在尝试删除驱动程序包之前,这些删除方法将首先更新使用要删除的驱动程序包的任何设备,使其不再随该驱动程序包一起安装。

驱动程序包开发

测试专用二进制文件

开发驱动程序包时,如果需要将驱动程序包中的特定可执行文件替换为专用版本,而不是完全重建和替换系统上的驱动程序包,则建议将内核调试器与 .kdfiles 命令一起使用。 由于驱动程序存储区中文件的完整路径不应硬编码,因此建议在 .kdfiles 映射中,OldDriver 文件名仅为文件的直接名称,前面不含路径信息。 为了便于实现此目的(和其他情况),驱动程序包中的文件名应尽可能唯一,以便与系统上不相关的驱动程序包中的文件名不匹配。

移植 INF 以使用“从驱动程序存储区运行”

如果现有的驱动程序包的 INF 不使用从驱动程序存储区中运行,并且正在将其移植到使用从驱动程序存储区运行,则以下示例显示了 INF 中的一些常见文件用法以及更新这些文件以从 DriverStore 运行的模式。

目标目录更新的快速参考

下表提供了根据驱动程序包 INF 为文件指定的当前目标目录 DIRID 查找相应指南的快速参考。

DIRID 子目录 详细信息
13 该文件已在使用“从驱动程序存储区运行”。 无需执行其他操作。
1 不得使用 DIRID 1。 在需要解析对文件的引用时,无法保证源目录可用。 相反,如果驱动程序包中的组件依赖于特定文件,则应在驱动程序包中包含这些文件,并从驱动程序存储区运行这些文件。
10 固件 有关如何将 DIRID 13 与固件更新驱动程序包配合使用以使其使用“从驱动程序存储区运行”的信息,请参阅创作更新驱动程序包
10 请参阅其他文件
11 请参阅其他文件
12 UMDF 请参阅 UMDF 驱动程序二进制文件
12 大多数目标为 DIRID 12 的文件都代表驱动程序服务二进制文件。 请参阅服务二进制文件
16422、16426、16427、16428 具有这些 DIRID 目标的大多数文件都代表安装应用程序。 相反,则提供通用 Windows 平台 (UWP) 应用程序,并使用驱动程序包 INF 的 DDInstall.Software 部分中的 AddSoftware 指令进行安装。 有关详细信息,请参阅将驱动程序与通用 Windows 平台 (UWP) 应用配对

服务二进制文件

如果 INF 添加了一项服务,并且二进制文件未从驱动程序存储区运行,则 INF 可能如下所示:

[DestinationDirs]
 ; Copy the file to %windir%\system32\drivers
 Example_CopyFiles = 12

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleBinary.sys

[ExampleDDInstall.Services]
AddService = ExampleService,0x2,Example_Service_Inst

[Example_Service_Inst]
DisplayName   = %SvcDesc%
ServiceType   = %SERVICE_KERNEL_DRIVER%
StartType     = %SERVICE_DEMAND_START%
ErrorControl  = %SERVICE_ERROR_NORMAL%
; Point at the file in %windir%\system32\drivers
ServiceBinary = %12%\ExampleBinary.sys

要移动此文件以从从驱动程序存储区运行,将需要更新 DestinationDirs 条目以了解文件将被复制到的位置,并更新引用此文件位置的 ServiceBinary 指令。

[DestinationDirs]
; Update the destination to DIRID 13
Example_CopyFiles = 13

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleBinary.sys

[ExampleDDInstall.Services]
AddService = ExampleService,0x2,Example_Service_Inst

[Example_Service_Inst]
DisplayName   = %SvcDesc%
ServiceType   = %SERVICE_KERNEL_DRIVER%
StartType     = %SERVICE_DEMAND_START%
ErrorControl  = %SERVICE_ERROR_NORMAL%
; Point at the run from Driver Store file using DIRID 13
ServiceBinary = %13%\ExampleBinary.sys

UMDF 驱动程序二进制文件

如果 INF 添加了一项 UMDF 驱动程序,并且二进制文件未从驱动程序存储区运行,则 INF 可能如下所示:

[DestinationDirs]
; Copy the file to %windir%\system32\drivers\UMDF
Example_CopyFiles = 12, UMDF

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleUmdfDriver.dll

[ExampleDDInstall.Wdf]
UmdfService = ExampleUmdfDriver,Example_UMDF_Inst
...

[Example_UMDF_Inst]
; Point at the file in %windir%\system32\drivers\UMDF
ServiceBinary = %12%\UMDF\ExampleUmdfDriver.dll
...

要移动此文件以从从驱动程序存储区运行,将需要更新 DestinationDirs 条目以了解文件将被复制到的位置,并更新引用此文件位置的 ServiceBinary 指令。

[DestinationDirs]
; Update the destination to DIRID 13
Example_CopyFiles = 13

[ExampleDDInstall]
CopyFiles = Example_CopyFiles

[Example_CopyFiles]
ExampleUmdfDriver.dll

[ExampleDDInstall.Wdf]
UmdfService = ExampleUmdfDriver,Example_UMDF_Inst
...

[Example_UMDF_Inst]
; Point at the run from Driver Store file using DIRID 13
ServiceBinary = %13%\ExampleUmdfDriver.dll
...

其他文件

如果 INF 添加的文件可能由其他组件加载,并且不是从驱动程序存储区运行的,则 INF 可能如下所示。 在此示例中,仅将文件名称写入设备的注册表状态。 读取此注册表值以确定要加载哪个文件的组件将取决于文件是否位于 %windir%\system32 中或取决于 LoadLibrary 的搜索顺序是否能够找到该文件。

[DestinationDirs]
; Copy the file to %windir%\system32
Example_CopyFiles = 11

[ExampleDDInstall]
CopyFiles=Example_CopyFiles
AddReg=Example_AddReg

[Example_CopyFiles]
ExampleFile.dll

[Example_AddReg]
HKR,,FileLocation,,"ExampleFile.dll"

要移动此文件以从驱动程序存储区运行,将需要更新 DestinationDirs 条目以了解文件将被复制到的位置,并更新保存在设备状态中的位置。 这要求读取该注册表值的组件能够处理该注册表值作为文件的完整路径,而不是相对于 %windir%\system32 的文件。

[DestinationDirs]
Example_CopyFiles = 13 ; update the destination to DIRID 13

[ExampleDDInstall]
CopyFiles=Example_CopyFiles
AddReg=Example_AddReg

[Example_CopyFiles]
ExampleFile.dll

[Example_AddReg]
; Point at the run from Driver Store file using DIRID 13
HKR,,FileLocation,,"%13%\ExampleFile.dll"