驱动程序包隔离
驱动程序包隔离是 Windows 驱动程序的一项要求,让驱动程序包对外部变化更有弹性、更容易更新和安装。
注意
尽管驱动程序包隔离是针对 Windows 驱动程序的要求,但 Windows 桌面驱动程序仍可从中受益,因为此要求提高了复原能力和可维护性。
下表在左侧列中显示了 Windows 驱动程序不再允许的旧驱动程序包做法的示例,在右侧列中显示了要求的 Windows 驱动程序行为。
非独立驱动程序 | 独立驱动程序 |
---|---|
INF 将文件复制到 %windir%\System32 or %windir%\System32\drivers | 驱动程序文件从驱动程序存储区运行 |
使用硬编码路径与设备堆栈/驱动程序交互 | 使用系统提供的功能或设备接口与设备堆栈/驱动程序交互 |
将路径硬编码到全局注册表位置 | 使用 HKR 和系统提供的功能获取注册表和文件状态的相对位置 |
运行时文件写入任何位置 | 文件是相对于操作系统所提供的位置编写的 |
有关确定驱动程序包是否满足驱动程序包隔离要求的帮助,请参阅验证 Windows 驱动程序。 有关如何更新 INF 以满足驱动程序包隔离要求的示例,请参阅移植 INF 以遵循驱动程序包隔离。
从驱动程序存储区运行
所有独立驱动程序包都将其驱动程序包文件保留在驱动程序存储区中。 这意味着,它们在其 INF 中指定 DIRID 13 以在安装时指定驱动程序包文件的位置。 要详细了解如何在驱动程序包中使用它,请参阅从驱动程序存储区运行。
读取和写入状态
注意
如果组件使用的是设备或设备接口属性来存储状态,则继续使用该方法和适当的 OS API 来存储并访问状态。 以下有关注册表和文件状态的指南适用于需要由组件存储的其他状态。
对各种注册表和文件状态的访问应该通过调用函数来完成,这些函数向调用方提供状态的位置,然后系统将读取/写入与该位置相关的状态。 请勿使用硬编码的绝对注册表路径和文件路径。
本部分包含以下小节:
注册表状态
本部分包含以下小节:
PnP 设备注册表状态
独立驱动程序包和用户模式组件通常使用两个位置之一在注册表中存储设备状态。 它们是用于设备的硬件密钥(设备密钥)和用于设备的软件密钥(驱动程序密钥)。 硬件键通常适用于有关单个设备实例如何与硬件交互的设置。 例如,启用硬件功能或将硬件置于特定模式。 软件键通常适用于有关单个设备实例如何与系统和其他软件交互的设置。 例如,配置数据文件的位置、与框架交互操作或访问设备的应用设置。 要检索这些注册表位置的句柄,请使用以下选项之一:
IoOpenDeviceRegistryKey (WDM)
CM_Open_DevNode_Key(用户模式代码)
INF AddReg 指令,使用引用自 INF DDInstall 部分或 DDInstall.HW 部分的 add-registry-section 中的 HKR reg-root 条目,如下所示:
[ExampleDDInstall.HW]
AddReg = Example_DDInstall.AddReg
[Example_DDInstall.AddReg]
HKR,,ExampleValue,,%13%\ExampleFile.dll
设备接口注册表状态
要读取和写入设备接口注册表状态,请使用以下选项之一:
CM_Open_Device_Interface_Key(用户模式代码)
INF AddReg 指令,使用引用自 add-interface-section 部分的 add-registry-section 中的 HKR reg-root 条目
服务注册表状态
服务状态应分为 3 个类别之一
不可变服务注册表状态
不可变服务状态是安装服务的驱动程序包提供的状态。 INF 为驱动程序和 Win32 服务设置的这些注册表值必须存储在服务的“参数”子项下,方法是在 AddReg 部分中提供 HKR 行,然后在 INF 的服务安装部分引用该部分。 例如:
[ExampleDDInstall.Services]
Addservice = ExampleService, 0x2, Example_Service_Inst
[Example_Service_Inst]
DisplayName = %ExampleService.SvcDesc%
ServiceType = 1
StartType = 3
ErrorControl = 1
ServiceBinary = %13%\ExampleService.sys
AddReg=Example_Service_Inst.AddReg
[Example_Service_Inst.AddReg]
HKR, Parameters, ExampleValue, 0x00010001, 1
要在运行时从服务访问此状态的位置,请使用以下函数之一:
IoOpenDriverRegistryKey (WDM),DRIVER_REGKEY_TYPE 为 DriverRegKeyParameters
GetServiceRegistryStateKey(Win32 服务),SERVICE_REGISTRY_STATE_TYPE 为 ServiceRegistryStateParameters
这些由 INF 在服务的“参数”子键中提供的注册表值只能在运行时读取,不能修改。 应将其视为只读。
如果 INF 提供的注册表值是可在运行时替代的默认设置,则应将替代值写入服务的内部服务注册表状态或共享服务注册表状态。 检索该设置时,可以首先在可变状态中进行查找。 如果不存在,则可以在不可变状态中查找该设置。 RtlQueryRegistryValueWithFallback 可用于帮助查询设置,例如具有替代和默认值的设置。
内部服务注册表状态
内部服务状态是在运行时写入的状态,仅由服务本身拥有和管理,并且仅可供该服务访问。 要访问内部服务状态的位置,请从服务使用以下函数之一:
IoOpenDriverRegistryKey (WDM),DRIVER_REGKEY_TYPE 为 DriverRegKeyPersistentState
GetServiceRegistryStateKey(Win32 服务),SERVICE_REGISTRY_STATE_TYPE 为 ServiceRegistryStatePersistent
如果服务想要允许其他组件修改这些设置,则该服务必须公开一个接口,另一个组件可以调用该接口来告知服务如何更改这些设置。 例如,Win32 服务可以公开 COM 或 RPC 接口,驱动程序服务可以通过设备接口公开 IOCTL 接口。
共享服务注册表状态
共享服务状态是在运行时写入的状态,如果其他用户模式组件具有足够的特权,则可以与它们共享。 要访问此共享服务状态的位置,请使用以下函数之一:
IoOpenDriverRegistryKey (WDM),DRIVER_REGKEY_TYPE 为 DriverRegKeySharedPersistentState
GetSharedServiceRegistryStateKey(Win32 服务),SERVICE_SHARED_REGISTRY_STATE_TYPE 为 ServiceSharedRegistryPersistentState
文件状态
本部分包含以下小节:
设备文件状态
如果需要在运行时写入与设备相关的文件,这些文件应相对于通过 OS API 提供的句柄或文件路径进行存储。 例如,特定于该设备的配置文件就是一个应存储在此处的文件类型。 要访问此状态的位置,请从服务使用以下函数之一:
IoGetDeviceDirectory (WDM),DirectoryType 参数设置为 DeviceDirectoryData
服务文件状态
服务文件状态可以分为 3 个类别之一
不可变服务文件状态
不可变服务文件状态是作为驱动程序包的一部分的文件。 有关访问这些文件的详细信息,请参阅从驱动程序存储区运行。
内部服务文件状态
内部服务文件状态是在运行时写入的状态,仅由服务本身拥有和管理,并且仅可供该服务访问。 要访问内部服务状态的位置,请从服务使用以下函数之一:
IoGetDriverDirectory (WDM, KMDF),DirectoryType 参数设置为 DriverDirectoryData
GetServiceDirectory(Win32 服务),eDirectoryType 参数设置为 ServiceDirectoryPersistentState
如果服务想要允许其他组件修改这些设置,则该服务必须公开一个接口,另一个组件可以调用该接口来告知服务如何更改这些设置。 例如,Win32 服务可以公开 COM 或 RPC 接口,驱动程序服务可以通过设备接口公开 IOCTL 接口。
共享服务文件状态
共享服务文件状态是在运行时写入的状态,如果其他用户模式组件具有足够的特权,则可以与它们共享。 要访问此共享服务状态的位置,请使用以下函数之一:
IoGetDriverDirectory (WDM, KMDF),DirectoryType 参数设置为 DriverDirectorySharedData
GetSharedServiceDirectory(Win32 服务),DirectoryType 参数设置为 ServiceSharedDirectoryPersistentState
DriverData 和 ProgramData
可与其他组件共享但不属于共享服务文件状态类别的文件可被写入 DriverData
或 ProgramData
位置。
这些位置为组件提供一个位置,用于写入临时状态或旨在由其他组件使用、可能从某一系统收集和复制以供另一系统处理的状态。 例如,自定义日志文件或故障转储符合此描述。
避免在 DriverData
或 ProgramData
目录的根目录中写入文件。 请改用公司名称创建子目录,然后在在该目录中写入文件和更多子目录。
例如,对于 Contoso 的公司名称,内核模式驱动程序可将自定义日志写入 \DriverData\Contoso\Logs
,用户模式应用程序可收集或分析 %DriverData%\Contoso\Logs
中的日志文件。
DriverData
Windows 10 版本 1803 及更高版本中提供了 DriverData
目录,可供管理员和 UMDF 驱动程序访问。
内核模式驱动程序使用系统提供的名为 \DriverData
的符号链接访问 DriverData
目录。
用户模式程序使用环境变量 %DriverData%
访问 DriverData
目录。
ProgramData
%ProgramData%
用户模式环境变量适用于存储数据时要使用的用户模式组件。
临时文件
临时文件通常被用于中间操作。 这些内容可以写入 %TEMP%
或 %TMP%
环境变量下的子路径。 由于这些位置是通过环境变量访问的,因此这一功能仅限于用户模式组件。 在这些临时文件的句柄被关闭后,无法保证它们的生存期或持久性。 操作系统或用户可以随时删除它们,而且它们在重启后也不会继续存在。
避免在 %TEMP%
或 %TMP%
目录的根目录中写入文件。 请改用公司名称创建子目录,然后在在该目录中写入文件和更多子目录。
属性状态
设备和设备接口都支持通过 PnP 属性模型来存储状态。 属性模型允许针对设备或设备接口存储结构化的属性数据。 这适用于合理容纳属性模型支持的属性类型的小数据。
要访问设备属性,可以使用以下 API:
WDM 驱动程序
WDF 驱动程序
用户模式代码
要访问设备接口属性,可以使用以下 API:
WDM 驱动程序
WDF 驱动程序
用户模式代码
使用设备接口
如果驱动程序想要允许其他组件读取或修改驱动程序的内部状态,则驱动程序应公开一个接口,另一个组件可以调用该接口,告知驱动程序要返回哪些设置或如何修改特定设置。 例如,驱动程序服务可以通过设备接口公开 IOCTL 接口。
通常,拥有该状态的驱动程序会在自定义设备接口类中公开一个设备接口。 当驱动程序准备好让其他组件访问该状态时,它将启用该接口。 要在启用设备接口时获得通知,用户模式组件可以注册设备接口到达通知,内核模式组件可以使用 IoRegisterPlugPlayNotification。 为了使这些组件能够访问状态,启用接口的驱动程序必须为其自定义设备接口类定义一个协议。 此协议通常是以下两种类型之一:
I/O 协议,可以与提供访问状态的机制的设备接口类相关联。 其他组件使用已启用的设备接口来发送符合协议的 I/O 请求。
直接调用接口通过查询接口返回。 其他驱动程序可以通过发送 IRP_MN_QUERY_INTERFACE 从驱动程序检索函数指针以进行调用。
或者,如果拥有该状态的驱动程序允许直接访问该状态,其他驱动程序则可以通过使用系统提供的功能以编程方式访问设备接口状态来访问该状态。 有关详细信息,请参阅设备接口注册表状态。
这些接口或状态(取决于使用的共享方法)需要进行正确的版本控制,以便拥有该状态的驱动程序可以独立于访问该状态的其他组件而获得服务。 驱动程序供应商不能依赖于与驱动程序同时获得服务并保持相同版本的其他组件。
因为控制接口的设备和驱动程序变化不定,所以驱动程序和应用程序应该避免在组件启动时调用 IoGetDeviceInterfaces 来获取已启用接口的列表。 最佳做法是注册设备接口到达或删除的通知,然后调用适当的函数来获取计算机上现有的已启用接口的列表。
有关设备接口的详细信息,请参阅:
有关状态管理 API 的操作系统支持的快速参考
大多数驱动程序包都需要支持一系列操作系统版本。 有关如何在驱动程序包中实现此目的的详细信息,请参阅支持多个操作系统版本。 下表提供了为各种状态管理 API 添加的操作系统支持的快速参考。
WDM 驱动程序
操作系统 | 添加了支持 |
---|---|
Windows 2000 | IoOpenDeviceRegistryKey IoOpenDeviceInterfaceRegistryKey |
Windows Vista | IoGetDevicePropertyData IoSetDevicePropertyData |
Windows 8 | IoGetDeviceInterfacePropertyData IoSetDeviceInterfacePropertyData |
Windows 8.1 | IoQueryFullDriverPath |
Windows 10 1803 | IoOpenDriverRegistryKey,针对 DriverRegKeyParameters 和 DriverRegKeyPersistentState 的 RegKeyType IoGetDeviceDirectory IoGetDriverDirectory,针对 DriverDirectoryImage 和 DriverDirectoryData 的 DirectoryType |
Windows 10 1809 | RtlQueryRegistryValueWithFallback |
Windows 11 21H2 | IoOpenDriverRegistryKey,针对 DriverRegKeySharedPersistentState 的 RegKeyType IoGetDriverDirectory,针对 DriverDirectorySharedData 的 DirectoryType |
KMDF 驱动程序
UMDF 驱动程序
用户模式代码
操作系统 | 添加了支持 |
---|---|
Windows 2000 | CM_Open_DevNode_Key |
Windows Vista | CM_Open_Device_Interface_Key CM_Get_DevNode_Property CM_Set_DevNode_Property CM_Get_Device_Interface_Property CM_Set_Device_Interface_Property |
Windows 10 2004 | GetServiceRegistryStateKey GetServiceDirectory |
Windows 11 21H2 | GetSharedServiceRegistryStateKey GetSharedServiceDirectory |