动态链接库重定向
DLL 加载程序是操作系统 (操作系统) 的一部分,用于解析对 DLL 的引用、加载和链接 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 重定向。 这是因为它不需要对应用本身进行任何更改。 但是,如果要创建新应用或更新现有应用,并且想要将应用与潜在问题隔离开来,请创建并排组件。
可选:如果具有应用清单) ,请配置注册表 (
另一种重定向是,应用将应用程序清单 (也称为并行应用程序清单或融合清单) ,或者应用具有 ID RT_MANIFEST类型 1 的 Win32 资源。 有关详细信息,请参阅 清单。
如果应用使用此类重定向,则任何 DotLocal 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
则只需执行几个简单的步骤即可在目标设备上手动实现相同的目的。
确定包的安装文件夹。 可以通过发出 命令
Get-AppxPackage
并在 PowerShell 中查找返回的 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) 目录
application.local
的任何 DLL,然后[重新]启动应用。
验证一切是否正常工作
若要确认在运行时加载正确的 DLL,可以使用 Visual Studio 并附加调试器。
- 打开“ 模块 ”窗口, (调试>Windows>模块) 。
- 找到 DLL,并确保 路径 指示重定向的副本,而不是系统部署的版本。
- 确认仅加载给定 DLL 的一个副本。
如何重定向未打包应用的 DLL
重定向文件必须命名为 <your_app_name>.local
。 因此,如果应用的名称为 Editor.exe
,则将重定向文件 Editor.exe.local
命名为 。 必须在可执行文件的 文件夹中安装重定向文件。 还必须在可执行文件的 文件夹中安装 DLL。
重定向文件 的内容 将被忽略;它的存在会导致 DLL 加载程序在加载 DLL 时首先检查可执行文件的文件夹。 为了缓解 COM 问题,该重定向同时应用于完整路径加载和部分名称加载。 因此,重定向发生在 COM 案例中,也不管指定到 LoadLibrary 或 LoadLibraryEx 的路径如何。 如果在可执行文件的 文件夹中找不到 DLL,则加载将遵循其通常的搜索顺序。 例如,如果应用 C:\myapp\myapp.exe
使用以下路径调用 LoadLibrary :
C:\Program Files\Common Files\System\mydll.dll
如果 和 C:\myapp\mydll.dll
都C:\myapp\myapp.exe.local
存在,则 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 (副本,也不会导致应用) 失败。