開始使用套件支持架構

套件支援架構是一個 開放原始碼 套件,可協助您將修正程式套用至現有的傳統型應用程式(而不修改程式代碼),以便在 MSIX 容器中執行。 套件支援架構有助於讓應用程式遵循最新執行階段環境的最佳做法。

本文提供套件支持架構的每個元件,以及使用它的逐步指南。

瞭解套件支持架構內的內容

套件支援架構包含一個可執行檔、一個執行階段管理員 DLL,以及一組執行階段修正程式。

Package Support Framework

以下是程式:

  1. 建立組態檔,指定您要套用至應用程式的修正程式。
  2. 修改套件以指向套件支援架構 (PSF) 啟動器可執行檔。

當使用者啟動您的應用程式時,套件支援架構啟動器是執行的第一個可執行檔。 該啓動器會讀取組態檔,並將執行階段修正程式和執行階段管理員 DLL 插入至應用程式處理程序。 當應用程式有需要時,執行階段管理員就會套用修正程式,以在 MSIX 容器內執行應用程式。

Package Support Framework DLL Injection

步驟 1:識別封裝的應用程式相容性問題

首先,為您的應用程式建立套件。 然後,安裝它、執行它,並觀察其行為。 您可能會收到可協助您找出相容性問題的錯誤訊息。 您也可以使用處理程序監視器來找出問題。 常見問題與有關工作目錄和程式路徑許可權的應用程式假設有關。

使用行程監視器來識別問題

進程監視器 是觀察應用程式檔案和登錄作業及其結果的強大公用程式。 這可協助您瞭解應用程式相容性問題。 開啟行程監視器之後,新增篩選條件 [篩選 > ...],只包含來自應用程式可執行檔的事件。

ProcMon App Filter

隨即會出現事件清單。 對於其中許多事件,SUCCESS 一詞會出現在 [結果] 數據行中。

ProcMon Events

您可以選擇性地篩選事件,只顯示失敗。

ProcMon Exclude Success

如果您懷疑文件系統存取失敗,請搜尋 System32/SysWOW64 或套件檔案路徑下失敗的事件。 篩選也可以在這裡有所説明。 從此清單底部開始,向上捲動。 此清單底部出現的失敗最近發生。 請最注意包含字串的錯誤,例如「拒絕存取」和「找不到路徑/名稱」,並忽略看起來不可疑的專案。 PSFSample 有兩個問題。 您可以在下列影像中顯示的清單中看到這些問題。

ProcMon Config.txt

在此影像中顯示的第一個問題中,應用程式無法從位於 “C:\Windows\SysWOW64” 路徑的 “Config.txt” 檔案讀取。 應用程式不太可能嘗試直接參考該路徑。 最有可能的是,它嘗試使用相對路徑從該檔案讀取,且根據預設,“System32/SysWOW64” 是應用程式的工作目錄。 這表示應用程式預期其目前的工作目錄設定為封裝中的某個位置。 在 appx 內部查看,我們可以看到檔案存在於與可執行檔相同的目錄中。

App Config.txt

第二個問題會出現在下圖中。

ProcMon Logfile

在此問題中,應用程式無法將 .log 檔案寫入其套件路徑。 這表示檔案重新導向修正可能會有所説明。

步驟 2:尋找運行時間修正

PSF 包含您現在可以使用的運行時間修正,例如檔案重新導向修正。

檔案重新導向修正

您可以使用檔案重新 導向修正 ,將嘗試寫入或讀取目錄中無法從 MSIX 容器中執行的應用程式存取的數據重新導向。

例如,如果您的應用程式寫入與應用程式可執行檔位於相同目錄中的記錄檔,您可以使用 [ 檔案重新導向修正] 在另一個位置建立該記錄檔,例如本機應用程式數據存放區。

來自社群的運行時間修正

請務必檢閱社群 對 GitHub 頁面的貢獻。 其他開發人員可能已解決類似您的問題,並已共用運行時間修正程式。

步驟 3:套用運行時間修正

您可以使用 Windows SDK 中的一些簡單工具套用現有的運行時間修正,並遵循下列步驟。

  • 建立套件配置資料夾
  • 取得套件支援架構檔案
  • 將它們新增至您的套件
  • 修改套件指令清單
  • 建立組態檔

讓我們逐一查看每個工作。

建立套件配置資料夾

如果您已經有 .msix (或 .appx) 檔案,您可以將內容解壓縮到配置資料夾,做為套件的暫存區域。 您可以使用MakeAppx工具從命令提示字元執行此動作, 根據 SDK 的安裝路徑,您可以在 Windows 10 計算機上找到 makeappx.exe 工具:x86:C:\Program Files (x86)\Windows Kits\10\bin\x86\makeappx.exe x64:C:\Program Files (x86)\Windows Kits\10\bin\x64\makeappx.exe

makeappx unpack /p PSFSamplePackage_1.0.60.0_AnyCPU_Debug.msix /d PackageContents

這會為您提供如下所示的內容。

Package Layout

如果您沒有開頭的 .msix (或 .appx) 檔案,您可以從頭開始建立套件資料夾和檔案。

取得套件支援架構檔案

您可以使用獨立 Nuget 命令行工具,或透過 Visual Studio 取得 PSF Nuget 套件。

使用命令行工具取得套件

從這個位置安裝 Nuget 命令行工具: https://www.nuget.org/downloads。 然後,從 Nuget 命令行執行此命令:

nuget install Microsoft.PackageSupportFramework

或者,您可以將套件延伸模組重新命名為 .zip,並將它解壓縮。 您需要的所有檔案都會位在 /bin 資料夾底下。

使用 Visual Studio 取得套件

在 Visual Studio 中,以滑鼠右鍵按兩下您的方案或項目節點,然後挑選其中一個 [管理 Nuget 套件] 命令。 搜尋 Microsoft.PackageSupportFrameworkPSF ,以尋找 Nuget.org 上的套件。然後,安裝它。

將套件支援架構檔案新增至您的套件

將所需的 32 位和 64 位 PSF DLL 和可執行檔新增至套件目錄。 使用下列資料表作為指南。 您也會想要包含您需要的任何運行時間修正。 在我們的範例中,我們需要檔案重新導向運行時間修正。

應用程式可執行檔為 x64 應用程式可執行檔為 x86
PSFLauncher64.exe PSFLauncher32.exe
PSFRuntime64.dll PSFRuntime32.dll
PSFRunDll64.exe PSFRunDll32.exe

您的套件內容現在看起來應該像這樣。

Package Binaries

修改套件指令清單

在文本編輯器中開啟套件指令清單,然後將 專案的 屬性Application設定Executable為 PSF 啟動器可執行檔的名稱。 如果您知道目標應用程式的架構,請選取適當的版本 PSFLauncher32.exe 或 PSFLauncher64.exe。 如果沒有,PSFLauncher32.exe 在所有情況下都會運作。 以下是範例。

<Package ...>
  ...
  <Applications>
    <Application Id="PSFSample"
                 Executable="PSFLauncher32.exe"
                 EntryPoint="Windows.FullTrustApplication">
      ...
    </Application>
  </Applications>
</Package>

建立組態檔

建立檔名 config.json,並將該檔案儲存至套件的根資料夾。 修改 config.json 檔案的宣告應用程式識別碼,以指向您剛才取代的可執行檔。 使用從行程監視器取得的知識,您也可以設定工作目錄,以及使用檔案重新導向修正,將套件相對 “PSFSampleApp” 目錄下的讀取/寫入重新導向重新導向至 .log 檔案。

{
    "applications": [
        {
            "id": "PSFSample",
            "executable": "PSFSampleApp/PSFSample.exe",
            "workingDirectory": "PSFSampleApp/"
        }
    ],
    "processes": [
        {
            "executable": "PSFSample",
            "fixups": [
                {
                    "dll": "FileRedirectionFixup.dll",
                    "config": {
                        "redirectedPaths": {
                            "packageRelative": [
                                {
                                    "base": "PSFSampleApp/",
                                    "patterns": [
                                        ".*\\.log"
                                    ]
                                }
                            ]
                        }
                    }
                }
            ]
        }
    ]
}

以下是 config.json 架構的指南:

陣列 索引鍵
應用程式 id 使用封裝指令清單中專案之 屬性Application的值Id
應用程式 可執行檔 您要啟動之可執行檔的套件相對路徑。 在大部分情況下,您可以在修改套件指令清單檔案之前,先從套件指令清單檔案取得此值。 它是 Executable 元素的 Application 屬性值。
應用程式 workingDirectory (選擇性)封裝相對路徑,用來作為啟動之應用程式的工作目錄。 如果您未設定此值,作業系統會 System32 使用 目錄作為應用程式的工作目錄。
程序 可執行檔 在大部分情況下,這會是上述設定的名稱 executable ,並移除路徑和擴展名。
fixups dll 要載入之修正的套件相對路徑 .msix/.appx。
fixups config (選擇性)控制修正 dll 的運作方式。 此值的確切格式會依修正依修正而有所不同,因為每個修正程式都可以將這個「Blob」解譯為想要。

applicationsprocessesfixups 索引鍵是陣列。 這表示您可以使用 config.json 檔案來指定多個應用程式、進程和修正 DLL。

封裝及測試應用程式

接下來,建立套件。

makeappx pack /d PackageContents /p PSFSamplePackageFixup.msix

然後,簽署它。

signtool sign /a /v /fd sha256 /f ExportedSigningCertificate.pfx PSFSamplePackageFixup.msix

如需詳細資訊,請參閱 如何建立套件簽署憑證 ,以及如何 使用 signtool 簽署套件

使用 PowerShell 安裝套件。

注意

請記得先卸載套件。

powershell Add-AppPackage .\PSFSamplePackageFixup.msix

執行應用程式,並觀察套用運行時間修正的行為。 視需要重複診斷和封裝步驟。

檢查套件支持架構是否正在執行

您可以檢查您的執行時間修正是否正在執行。 若要這樣做,請開啟 [任務管理器 ],然後按兩下 [ 更多詳細數據]。 尋找已套用套件支援架構的應用程式,並展開應用程式詳細數據以提供更多詳細數據。 您應該能夠檢視套件支援架構正在執行。

使用追蹤修正程式

診斷已封裝應用程式相容性問題的替代方法是使用追蹤修正程式。 此 DLL 隨附於 PSF,並提供應用程式行為的詳細診斷檢視,類似於進程監視器。 它專為顯示應用程式相容性問題而設計。 若要使用追蹤修正,請將 DLL 新增至套件、將下列片段新增至您的 config.json,然後封裝並安裝您的應用程式。

{
    "dll": "TraceFixup.dll",
    "config": {
        "traceLevels": {
            "filesystem": "allFailures"
        }
    }
}

根據預設,追蹤修正程式會篩選出可能視為「預期」的失敗。 例如,應用程式可能會嘗試無條件刪除檔案,而不檢查檔案是否已經存在,而忽略結果。 這有一些非預期失敗可能會篩選掉的不幸結果,因此在上述範例中,我們選擇從文件系統函式接收所有失敗。 我們這樣做是因為我們知道,從 Config.txt 檔案讀取嘗試失敗,並出現訊息「找不到檔案」。 這是經常觀察到的失敗,通常不會假設為非預期。 實際上,最好只篩選非預期的失敗,如果仍有問題仍無法識別,則回復到所有失敗。

根據預設,追蹤修正程序的輸出會傳送至附加的調試程式。 在此範例中,我們不會附加調試程式,而是會改用來自SysInternals的 DebugView 程式來檢視其輸出。 執行應用程式之後,我們可以看到與之前相同的失敗,這會將我們指向相同的運行時間修正。

TraceShim File Not Found

TraceShim Access Denied

偵錯、擴充或建立運行時間修正

您可以使用 Visual Studio 對運行時間修正進行偵錯、擴充運行時間修正,或從頭開始建立一個。 您必須執行這些動作才能成功。

  • 新增封裝專案
  • 新增運行時間修正的專案
  • 新增啟動 PSF 啟動器可執行文件的專案
  • 設定封裝專案

完成時,您的解決方案看起來會像這樣。

Completed solution

讓我們看看此範例中的每個專案。

Project 目的
DesktopApplicationPackage 此專案是以 Windows 應用程式封裝項目為基礎,它會輸出 MSIX 套件。
Runtimefix 這是 C++ 動態連結庫專案,其中包含一或多個做為運行時間修正的取代函式。
PSFLauncher 這是 C++ 空白專案。 此專案是收集套件支持架構之運行時間可散發檔案的位置。 它會輸出可執行檔。 該可執行檔是您啟動方案時所執行的第一件事。
WinFormsDesktopApplication 此專案包含傳統型應用程式的原始程式碼。

若要查看包含所有這些類型專案的完整範例,請參閱 PSFSample

讓我們逐步解說在解決方案中建立和設定每個這些專案的步驟。

建立套件解決方案

如果您還沒有傳統型應用程式的解決方案,請在 Visual Studio 中建立新的 空白解決方案

Blank solution

您也可以新增您擁有的任何應用程式專案。

新增封裝專案

如果您還沒有 Windows 應用程式封裝專案,請建立一個專案,並將其新增至您的解決方案。

Package project template

如需 Windows 應用程式封裝專案的詳細資訊,請參閱 使用 Visual Studio 封裝您的應用程式。

方案總管 中,以滑鼠右鍵按兩下封裝專案,選取 [編輯],然後將此專案新增至項目檔底部:

<Target Name="PSFRemoveSourceProject" AfterTargets="ExpandProjectReferences" BeforeTargets="_ConvertItems">
<ItemGroup>
  <FilteredNonWapProjProjectOutput Include="@(_FilteredNonWapProjProjectOutput)">
  <SourceProject Condition="'%(_FilteredNonWapProjProjectOutput.SourceProject)'=='<your runtime fix project name goes here>'" />
  </FilteredNonWapProjProjectOutput>
  <_FilteredNonWapProjProjectOutput Remove="@(_FilteredNonWapProjProjectOutput)" />
  <_FilteredNonWapProjProjectOutput Include="@(FilteredNonWapProjProjectOutput)" />
</ItemGroup>
</Target>

新增運行時間修正的專案

將 C++ 動態連結庫 (DLL) 專案新增至方案。

Runtime fix library

以滑鼠右鍵按兩下該專案,然後選擇 [ 屬性]。

在屬性頁中,尋找 [C++ 語言標準 ] 欄位,然後在該欄位旁邊的下拉式清單中,選取 [ISO C++17 標準](/std:c++17) 選項。

ISO 17 Option

以滑鼠右鍵按下該專案,然後在操作功能表中,選擇 [ 管理 Nuget 套件] 選項。 確定 [ 封裝來源] 選項設定為 [全部 ] 或 [nuget.org]。

按兩下該欄位旁的設定圖示。

搜尋 PSF* Nuget 套件,然後安裝此專案的套件。

nuget package

如果您想要偵錯或擴充現有的運行時間修正,請使用本指南的尋找運行時間修正一節中所述 的指引,新增您取得的運行時間修正 檔案。

如果您想要建立全新的修正程式,請勿將任何專案新增至此專案。 我們稍後將協助您在本指南中將正確的檔案新增至此專案。 目前,我們會繼續設定您的解決方案。

新增啟動 PSF 啟動器可執行文件的專案

將 C++ 空白專案 新增至方案。

Empty project

使用上一節所述的相同指引,將 PSF Nuget 套件新增至此專案。

開啟項目的屬性頁,然後在 [一般設定] 頁面中,將 [目標名稱] 屬性設定PSFLauncher32 ,或PSFLauncher64視應用程式的架構而定。

PSF Launcher reference

將項目參考新增至方案中的運行時間修正專案。

runtime fix reference

以滑鼠右鍵按兩下參考,然後在 [ 屬性] 視窗中套用這些值。

屬性
複製本機 True
複製附屬組件到本機 True
參考組件輸出 True
連結程式庫相依性 False
連結庫相依性輸入 False

設定封裝專案

在封裝專案中,以滑鼠右鍵按兩下 [應用程式] 資料夾,然後選擇 [ 新增參考]。

Add Project Reference

選擇 PSF 啟動器專案和傳統型應用程式專案,然後選擇 [ 確定] 按鈕。

Desktop project

注意

如果您沒有應用程式的原始程式碼,只要選擇 PSF 啟動器專案即可。 我們將示範如何在建立組態檔時參考可執行檔。

在 [ 應用程式] 節點中,以滑鼠右鍵按下 PSF 啟動器應用程式,然後選擇 [ 設定為進入點]。

Set entry point

將名為 config.json 的檔案新增至封裝專案,然後將下列 json 文字複製並貼到檔案中。 將 [ 封裝動作] 屬性設定為 [內容]。

{
    "applications": [
        {
            "id": "",
            "executable": "",
            "workingDirectory": ""
        }
    ],
    "processes": [
        {
            "executable": "",
            "fixups": [
                {
                    "dll": "",
                    "config": {
                    }
                }
            ]
        }
    ]
}

為每個索引鍵提供值。 使用此表格做為指南。

陣列 索引鍵
應用程式 id 使用封裝指令清單中專案之 屬性Application的值Id
應用程式 可執行檔 您要啟動之可執行檔的套件相對路徑。 在大部分情況下,您可以在修改套件指令清單檔案之前,先從套件指令清單檔案取得此值。 它是 Executable 元素的 Application 屬性值。
應用程式 workingDirectory (選擇性)封裝相對路徑,用來作為啟動之應用程式的工作目錄。 如果您未設定此值,作業系統會 System32 使用 目錄作為應用程式的工作目錄。
程序 可執行檔 在大部分情況下,這會是上述設定的名稱 executable ,並移除路徑和擴展名。
fixups dll 要載入之修正 DLL 的套件相對路徑。
fixups config (選擇性)控制修正 DLL 的運作方式。 此值的確切格式會依修正依修正而有所不同,因為每個修正程式都可以將這個「Blob」解譯為想要。

完成時,您的 config.json 檔案看起來會像這樣。

{
  "applications": [
    {
      "id": "DesktopApplication",
      "executable": "DesktopApplication/WinFormsDesktopApplication.exe",
      "workingDirectory": "WinFormsDesktopApplication"
    }
  ],
  "processes": [
    {
      "executable": ".*App.*",
      "fixups": [ { "dll": "RuntimeFix.dll" } ]
    }
  ]
}

注意

applicationsprocessesfixups 索引鍵是陣列。 這表示您可以使用 config.json 檔案來指定多個應用程式、進程和修正 DLL。

偵錯運行時間修正

在 Visual Studio 中,按 F5 以啟動調試程式。 首先,啟動的是 PSF 啟動器應用程式,接著會啟動您的目標傳統型應用程式。 若要偵錯目標傳統型應用程式,您必須選擇 [ 偵錯->附加至進程],然後選取應用程式進程,手動附加至桌面應用程式進程。 若要允許使用原生運行時間修正 DLL 偵錯 .NET 應用程式,請選取 Managed 和原生程式代碼類型 (混合模式偵錯)。

設定好之後,您可以在傳統型應用程式程式代碼和運行時間修正專案中設定程式代碼行旁的斷點。 如果您沒有應用程式的原始程式碼,您就只能在運行時間修正專案中的程式代碼行旁設定斷點。

由於 F5 偵錯會從套件設定資料夾路徑部署鬆散檔案,而不是從 .msix/.appx 套件安裝,因此配置資料夾通常沒有與已安裝套件資料夾相同的安全性限制。 因此,在套用運行時間修正之前,可能無法重現套件路徑存取拒絕錯誤。

若要解決此問題,請使用 .msix / .appx 套件部署,而不是 F5 鬆散檔案部署。 若要建立 .msix / .appx 套件檔案,請使用來自 Windows SDK 的 MakeAppx 公用程式,如上所述。 或者,從 Visual Studio 中,以滑鼠右鍵按兩下您的應用程式項目節點,然後選取 [市集 -> 建立應用程式套件]。

Visual Studio 的另一個問題是,它沒有內建支援附加至調試程式所啟動的任何子進程。 這使得在目標應用程式的啟動路徑中難以偵錯邏輯,Visual Studio 必須在啟動之後手動附加該邏輯。

若要解決此問題,請使用支持子進程附加的調試程式。 請注意,通常不可能將 Just-In-Time (JIT) 調試程式附加至目標應用程式。 這是因為大部分的 JIT 技術都牽涉到透過 ImageFileExecutionOptions 登錄機碼啟動調試程式來取代目標應用程式。 這會使 PSFLauncher.exe 用來將 FixupRuntime.dll 插入目標應用程式的繞道機制失敗。 WinDbg 包含在適用於 Windows偵錯工具中,並從 Windows SDK 取得,可支援子進程附加。 它現在也支援直接 啟動和偵錯 UWP 應用程式

若要將目標應用程式啟動偵錯為子進程,請啟動 WinDbg

windbg.exe -plmPackage PSFSampleWithFixup_1.0.59.0_x86__7s220nvg1hg3m -plmApp PSFSample

在提示字元中 WinDbg ,啟用子偵錯並設定適當的斷點。

.childdbg 1
g

(執行直到目標應用程式啟動並中斷至調試程式)

sxe ld fixup.dll
g

(執行直到載入修正 DLL)

bp ...

注意

PLMDebug 也可以用來在啟動時將調試程式附加至應用程式,也包含在 Windows 的偵錯工具中。 不過,使用比 WinDbg 現在提供的直接支持更為複雜。

支援

有任何疑問嗎? 請在 MSIX 技術社群網站上的套件支援架構 對話空間上詢問我們。