动态链接库重定向

DLL 加载程序是操作系统 (操作系统) 的一部分,用于解析对 DLL 的引用、加载和链接 DLL。 动态链接库 (DLL) 重定向是影响 DLL 加载程序行为并控制它实际加载的候选 DLL 之一的技术之一。

此功能的其他名称包括 .localDot LocalDotLocalDot 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 重定向。 这是因为它不需要对应用本身进行任何更改。 但是,如果要创建新应用或更新现有应用,并且想要将应用与潜在问题隔离开来,请创建并排组件。

可选:如果具有应用清单) ,请配置注册表 (

另一种重定向是,应用将应用程序清单 (也称为并行应用程序清单或融合清单) ,或者应用具有 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&gt;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 "&lt;A.dll&gt;" copy /y "&lt;A.dll&gt;" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
            if EXIST "&lt;B.dll&gt;" copy /y "&lt;B.dll&gt;" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;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>

让我们演练一下该配置的一些功能。

  1. 为 Visual Studio 不带调试 (或启动调试) 体验设置 PreBuildEvent

    <ItemDefinitionGroup>
      <PreBuildEvent>
    
  2. 确保中间目录中的文件夹结构正确。

    <!-- [[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
    
  3. 将本地生成的任何 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&gt;nul
      if EXIST "<path-to-local-dll>" copy /y "<path-to-local-dll>" $(IntDir)\microsoft.system.package.metadata\application.local 2&gt;nul
      </Command>
    </PreBuildEvent>
    
  4. 最后,指示想要在部署的包中包含特殊目录及其内容。

    <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 则只需执行几个简单的步骤即可在目标设备上手动实现相同的目的。

  1. 确定包的安装文件夹。 可以通过发出 命令 Get-AppxPackage并在 PowerShell 中查找返回的 InstallLocation 来执行此操作。

  2. 使用该 InstallLocation 更改 ACL 以允许自己创建文件夹/复制文件。 <InstallLocation>编辑此脚本中的占位符,并运行脚本:

    cd <InstallLocation>\Microsoft.system.package.metadata
    takeown /F . /A
    icacls  . /grant Administrators:F
    md <InstallLocation>\Microsoft.system.package.metadata\application.local
    
  3. 最后,手动复制已在本地 (生成且希望优先使用系统部署的 DLL) 目录 application.local 的任何 DLL,然后[重新]启动应用。

验证一切是否正常工作

若要确认在运行时加载正确的 DLL,可以使用 Visual Studio 并附加调试器。

  1. 打开“ 模块 ”窗口, (调试>Windows>模块) 。
  2. 找到 DLL,并确保 路径 指示重定向的副本,而不是系统部署的版本。
  3. 确认仅加载给定 DLL 的一个副本。

如何重定向未打包应用的 DLL

重定向文件必须命名为 <your_app_name>.local。 因此,如果应用的名称为 Editor.exe,则将重定向文件 Editor.exe.local命名为 。 必须在可执行文件的 文件夹中安装重定向文件。 还必须在可执行文件的 文件夹中安装 DLL。

重定向文件 的内容 将被忽略;它的存在会导致 DLL 加载程序在加载 DLL 时首先检查可执行文件的文件夹。 为了缓解 COM 问题,该重定向同时应用于完整路径加载和部分名称加载。 因此,重定向发生在 COM 案例中,也不管指定到 LoadLibraryLoadLibraryEx 的路径如何。 如果在可执行文件的 文件夹中找不到 DLL,则加载将遵循其通常的搜索顺序。 例如,如果应用 C:\myapp\myapp.exe 使用以下路径调用 LoadLibrary

C:\Program Files\Common Files\System\mydll.dll

如果 和 C:\myapp\mydll.dllC:\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 (副本,也不会导致应用) 失败。