動態連結程式庫重新導向

DLL 載入器 是作業系統 (OS) 的一部分,可解析 DLL 的參考、載入和連結 DLL。 動態連結程式庫 (DLL) 重新導向是其中一種技術,可讓您影響 DLL 載入器 的行為 ,並控制它實際載入的數個候選 DLL 之一。

這項功能的其他名稱包括 .local Dot Local DotLocal Dot Local 偵錯

DLL 版本控制問題

如果您的應用程式相依于特定版本的共用 DLL,而另一個應用程式則與較新或較舊版本的該 DLL 一起安裝,則這可能會導致相容性問題和不穩定:它可能會導致您的應用程式開始失敗。

DLL 載入器會先查看呼叫進程從 (可執行檔的資料夾) 載入的 資料夾中,再查看其他檔案系統位置。 因此,有一個因應措施是在可執行檔的 資料夾中安裝應用程式所需的 DLL。 這實際上會使 DLL 成為私人的。

但這並不能解決 COM 的問題。 可以安裝並註冊兩個不相容的 COM 伺服器版本(即使在不同的檔案系統位置中),但只有一個位置可以註冊 COM 伺服器。 因此,只會啟動最新的已註冊 COM 伺服器。

您可以使用重新導向來解決這些問題。

載入及測試私人二進位檔

DLL 載入器遵循的規則可確保系統 DLL 會從 Windows 系統位置載入,例如系統資料夾 ( %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&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. 判斷套件的安裝資料夾。 您可以在 PowerShell 中發出 命令 Get-AppxPackage ,並尋找傳回的 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(並想要以喜好方式使用系統部署的 DLL)複製到 application.local 目錄,然後 [re]啟動應用程式。

確認所有專案都正常運作

若要確認在執行時間載入正確的 DLL,您可以使用 Visual Studio 搭配附加的偵錯工具。

  1. 開啟 [模組 ] 視窗 ( > Windows > 模組)。
  2. 尋找 DLL,並確定 Path 指出重新導向的複本,而不是系統部署的版本。
  3. 確認只載入一份指定的 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 ,則 LoadLibrary 會載入 C:\myapp\mydll.dllC:\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 的複本(而且不會造成您的應用程式失敗)。