动态链接库重定向
DLL 加载程序 是作系统(OS)的一部分,它解析对 DLL 的引用、加载和链接它们。 动态链接库 (DLL) 重定向是一种可影响 DLL 加载程序的行为的技术之一,并控制它实际加载的几个候选 DLL 之一。
此功能的其他名称包括 .local、Dot Local、DotLocal和 Dot Local Debugging。
DLL 版本控制问题
如果应用程序依赖于共享 DLL 的特定版本,并且另一个应用程序随该 DLL 的较新版本或较旧版本一起安装,则这可能会导致兼容性问题和不稳定;它可能会导致应用启动失败。
DLL 加载程序在调用进程从(可执行文件的文件夹)加载,然后才能在其他文件系统位置中查找。 因此,一种解决方法是在可执行文件的文件夹中安装应用所需的 DLL。 这有效地使 DLL 成为私有的。
但这并不能解决 COM 的问题。 可以安装并注册两个不兼容版本的 COM 服务器(即使在不同的文件系统位置),但只有一个用于注册 COM 服务器的位置。 因此,只会激活最新的已注册 COM 服务器。
可以使用重定向来解决这些问题。
加载和测试专用二进制文件
DLL 加载程序遵循的规则可确保从 Windows 系统位置加载系统 DLL,例如系统文件夹(%SystemRoot%\system32
)。 这些规则避免了种植攻击:攻击者将编写的代码置于可以写入的位置,然后说服一些进程加载和执行它。 但是,加载程序的规则也使得在 OS 组件上工作更加困难,因为运行它们需要更新系统;这是一个非常有影响力的变化。
但是,可以使用重定向来加载 DLL 的专用副本(例如测试或测量代码更改的性能影响)。
如果要在公共 WindowsAppSDK GitHub 存储库中参与源代码,则需要测试更改。 同样,这是一种方案,你可以使用重定向来加载 DLL 的专用副本,而不是随 Windows 应用 SDK 一起提供的版本。
选项
事实上,有两种方法可以确保应用使用所需的 DLL 版本:
- DLL 重定向。 有关更多详细信息,请继续阅读本主题。
- 并排组件。 本主题 独立应用程序和并行程序集中所述。
提示
如果你是开发人员或管理员,则应对现有应用程序使用 DLL 重定向。 这是因为它不需要对应用本身进行任何更改。 但是,如果要创建新应用或更新现有应用,并且想要将应用与潜在问题隔离开来,则创建并排组件。
可选:配置注册表
若要启用 DLL 重定向计算机范围,必须创建新的注册表值。 在键 HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options
下,使用名称 DevOverrideEnable创建新的 DWORD 值。 将值设置为 1,然后重新启动计算机。 或使用以下命令(并重新启动计算机)。
reg add "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options" /v DevOverrideEnable /t REG_DWORD /d 1
设置注册表值后,即使应用具有应用程序清单,也遵循 DotLocal DLL 重定向。
创建重定向文件或文件夹
若要使用 DLL 重定向,你将创建 重定向文件 或 重定向文件夹(具体取决于你拥有的应用类型),如本主题后面的部分所示。
如何重定向打包应用的 DLL
打包的应用需要 DLL 重定向的特殊文件夹结构。 以下路径是启用重定向时加载程序将查找的位置:
<Drive>:\<path_to_package>\microsoft.system.package.metadata\application.local\
如果能够编辑 .vcxproj
文件,则使用包创建和部署该特殊文件夹的一种简便方法是在 .vcxproj
中为生成添加一些额外的步骤:
<ItemDefinitionGroup>
<PreBuildEvent>
<Command>
del $(FinalAppxManifestName) 2>nul
<!-- [[Using_.local_(DotLocal)_with_a_packaged_app]] This makes the extra DLL deployed via F5 get loaded instead of the system one. -->
if NOT EXIST $(IntDir)\microsoft.system.package.metadata\application.local MKDIR $(IntDir)\microsoft.system.package.metadata\application.local
if EXIST "<A.dll>" copy /y "<A.dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2>nul
if EXIST "<B.dll>" copy /y "<B.dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2>nul
</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<!-- Include any locally built system experience -->
<Media Include="$(IntDir)\microsoft.system.package.metadata\application.local\**">
<Link>microsoft.system.package.metadata\application.local</Link>
</Media>
</ItemGroup>
让我们演练一些配置的作用。
为 Visual Studio 启动而不调试(或 启动调试)体验设置
PreBuildEvent
。<ItemDefinitionGroup> <PreBuildEvent>
确保中间目录中具有正确的文件夹结构。
<!-- [[Using_.local_(DotLocal)_with_modern_apps]] This makes the extra DLL deployed via Start get loaded instead of the system one. --> if NOT EXIST $(IntDir)\microsoft.system.package.metadata\application.local MKDIR $(IntDir)\microsoft.system.package.metadata\application.local
将本地生成的任何 DLL(并希望优先使用系统部署的 DLL)复制到
application.local
目录中。 可以从任意位置选取 DLL(我们建议为.vcxproj
使用可用的宏)。 只需确保这些 DLL 在该项目之前生成;否则,它们将丢失。 此处显示了两个 模板 复制命令;根据需要使用任意数量的占位符,并编辑<path-to-local-dll>
占位符。if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2>nul if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2>nul </Command> </PreBuildEvent>
最后,指示你想要在部署的包中包含特殊目录及其内容。
<ItemGroup> <!-- Include any locally built system experience --> <Media Include="$(IntDir)\microsoft.system.package.metadata\application.local\**"> <Link>microsoft.system.package.metadata\application.local</Link> </Media> </ItemGroup>
此处所述的方法(使用中间目录)使源代码控制登记保持干净,并减少意外提交已编译二进制文件的可能性。
接下来,只需(重新)部署项目。 若要获得干净、完整(重新)的部署,可能还需要卸载/清除目标设备上的现有部署。
手动复制二进制文件
如果无法按照上面所示的方式使用 .vcxproj
,则可以使用几个简单的步骤在目标设备上实现相同的目的。
确定包的安装文件夹。 可以在 PowerShell 中发出命令
Get-AppxPackage
,并查找返回的 InstallLocation。使用该 InstallLocation 更改 ACL,以允许自己创建文件夹/复制文件。 编辑此脚本中的
<InstallLocation>
占位符,然后运行脚本:cd <InstallLocation>\Microsoft.system.package.metadata takeown /F . /A icacls . /grant Administrators:F md <InstallLocation>\Microsoft.system.package.metadata\application.local
最后,手动将本地生成的任何 DLL(并希望优先使用系统部署的 DLL)复制到
application.local
目录中,然后 [re]启动应用。
验证一切是否正常工作
若要确认在运行时加载正确的 DLL,可以将 Visual Studio 与附加的调试器一起使用。
- 打开 模块 窗口(调试>Windows>模块)。
- 找到 DLL,并确保 路径 指示重定向的副本,而不是系统部署的版本。
- 确认只加载给定 DLL 的一个副本。
如何重定向未打包应用的 DLL
重定向文件必须命名为 <your_app_name>.local
。 因此,如果应用的名称 Editor.exe
,请将重定向文件命名为 Editor.exe.local
。 必须在可执行文件的文件夹中安装重定向文件。 还必须在可执行文件的文件夹中安装 DLL。
重定向文件 内容将被忽略;它仅存在会导致 DLL 加载程序在加载 DLL 时先检查可执行文件的文件夹。 为了缓解 COM 问题,该重定向同时应用于完整路径和部分名称加载。 因此,无论指定 LoadLibrary 路径还是 LoadLibraryEx,重定向都会在 COM 案例中发生。 如果在可执行文件的文件夹中找不到 DLL,则加载遵循其通常的搜索顺序。 例如,如果应用 C:\myapp\myapp.exe
使用以下路径调用 LoadLibrary:
C:\Program Files\Common Files\System\mydll.dll
如果同时存在 C:\myapp\myapp.exe.local
和 C:\myapp\mydll.dll
,则 LoadLibrary 加载 C:\myapp\mydll.dll
。 否则,LoadLibrary 加载 C:\Program Files\Common Files\System\mydll.dll
。
或者,如果名为 C:\myapp\myapp.exe.local
的文件夹存在,并且它包含 mydll.dll
,则 LoadLibrary 加载 C:\myapp\myapp.exe.local\mydll.dll
。
如果使用 DLL 重定向,并且应用无权访问搜索顺序中的所有驱动器和目录,则 LoadLibrary 拒绝访问后立即停止搜索。 如果 未使用 DLL 重定向,LoadLibrary 会跳过无法访问的目录,然后继续搜索。
最好在包含应用的同一文件夹中安装应用 DLL;即使未使用 DLL 重定向。 这可确保安装应用不会覆盖 DLL 的其他副本(从而导致其他应用失败)。 此外,如果遵循此良好做法,则其他应用不会覆盖 DLL 的副本(并且不会导致应用失败)。