与包支持框架入门

包支持框架是一个开源工具包,可帮助你将修补程序应用于现有的桌面应用程序 (,而无需修改代码) 使其能够在 .msix 容器中运行。 包支持框架可帮助应用程序遵循新式运行时环境的最佳做法。

本文提供了有关包支持框架的每个组件的各异次,并逐步介绍了如何使用该组件。

了解包支持框架内的内容

包支持框架包含一个可执行文件、一个运行时管理器 DLL 和一组运行时修复程序。

包支持框架

过程如下:

  1. 创建一个配置文件,用于指定要应用于应用程序的修补程序。
  2. 将包修改为指向包支持框架 (PSF) 启动器可执行文件。

当用户启动应用程序时,包支持框架启动器是运行的第一个可执行文件。 该启动器将读取配置文件,并将运行时修复程序和运行时管理器 DLL 注入应用程序进程。 如果应用程序需要使用该修复程序才能在 MSIX 容器中运行,则运行时管理器会应用该修复程序。

包支持框架 DLL 注入

步骤1:识别打包的应用程序兼容性问题

首先,为应用程序创建包。 然后,安装它,运行它并观察它的行为。 收到的错误消息可能会帮助你识别兼容性问题。 也可以使用进程监视器来识别问题。 常见问题与有关工作目录和程序路径权限的应用程序假设相关。

使用进程监视器识别问题

进程监视器 是一个功能强大的实用工具,用于观察应用的文件和注册表操作及其结果。 这可以帮助你了解应用程序的兼容性问题。 打开进程监视器后,添加筛选器 (筛选器 > Filter ... ) 以便仅包括应用程序可执行文件中的事件。

Procmon 配合应用筛选器

将显示事件列表。 对于其中的许多事件,"结果" 列中将显示 "成功" 字样。

Procmon 配合事件

或者,您可以将事件筛选为仅显示失败。

Procmon 配合排除成功

如果你怀疑文件系统访问失败,请搜索 System32/SysWOW64 或包文件路径下的失败事件。 筛选器也可以在此处提供帮助。 从列表底部开始,向上滚动。 最近出现在此列表底部的失败。 请注意包含字符串(如 "拒绝访问" 和 "找不到路径/名称")的错误,并忽略看起来不可疑的问题。 PSFSample有两个问题。 在下图中显示的列表中可以看到这些问题。

Procmon 配合 Config.txt

在此图中出现的第一个问题中,应用程序无法从位于 "C:\ Windows \SysWOW64" 路径中的 "Config.txt" 文件中读取。 应用程序不太可能尝试直接引用该路径。 大多数情况下,它会尝试使用相对路径从该文件中读取,默认情况下,"System32/SysWOW64" 是应用程序的工作目录。 这表明,应用程序需要将当前工作目录设置为包中的某个位置。 查看 appx 内部,可以看到该文件与可执行文件位于同一目录中。

应用 Config.txt

下图显示了第二个问题。

Procmon 配合日志文件

在此问题中,应用程序无法将 .log 文件写入其包路径。 这会建议使用文件重定向修复方法。

步骤2:查找运行时修补程序

PSF 包含可立即使用的运行时修补程序,如文件重定向修正。

文件重定向修正

你可以使用 文件重定向修正 功能重定向尝试写入或读取目录中的数据,该目录无法从在 .msix 容器中运行的应用程序访问。

例如,如果你的应用程序写入到与你的应用程序可执行文件位于同一目录中的日志文件,则可以使用 文件重定向修正 功能在其他位置(例如本地应用数据存储)中创建该日志文件。

社区中的运行时修补程序

请确保查看GitHub页面的社区贡献。 其他开发人员可能会解决类似于你的问题并已共享运行时修复。

步骤3:应用运行时修补程序

您可以使用 Windows SDK 中的一些简单工具来应用现有运行时修补程序,并通过执行以下步骤。

  • 创建包布局文件夹
  • 获取包支持框架文件
  • 将它们添加到包
  • 修改包清单
  • 创建配置文件

让我们完成每个任务。

创建包布局文件夹

如果已 .msix (或 .appx) 文件,则可以将其内容解压缩到一个布局文件夹中,该文件夹将用作包的暂存区域。 你可以在命令提示符下使用 makeappx.exe 工具执行此操作,具体取决于 SDK 的安装路径,你可以在 Windows 10 PC 上找到 makeappx.exe 工具: x86: C:\Program files (x86) \ Windows Kits\10\bin\x86\makeappx.exe x64: C:\Program 文件 (x86) \ Windows Kits\10\bin\x64\makeappx.exe

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

这会显示如下所示的内容。

包布局

如果没有 .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 包" 命令之一。 搜索 " PackageSupportFramework " 或 " PSF " 以查找 Nuget.org 上的包。然后,安装它。

将包支持框架文件添加到包

将必需的32位和64位 PSF Dll 和可执行文件添加到包目录中。 使用下表作为指南。 还需要包含所需的任何运行时修补程序。 在我们的示例中,我们需要文件重定向运行时修复。

应用程序可执行文件为 x64 应用程序可执行文件为 x86
PSFLauncher64.exe PSFLauncher32.exe
PSFRuntime64.dll PSFRuntime32.dll
PSFRunDll64.exe PSFRunDll32.exe

包内容现在应如下所示。

包二进制文件

修改包清单

在文本编辑器中打开包清单,然后将 Executable 元素的属性设置 Application 为 PSF Launcher 可执行文件的名称。 如果你知道目标应用程序的体系结构,请选择适当的版本,PSFLauncher32.exe 或 PSFLauncher64.exe。 否则,PSFLauncher32.exe 将适用于所有情况。 下面是一个示例。

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

创建配置文件

创建文件名 config.json ,并将该文件保存到包的根文件夹。 修改文件上 config.js的已声明应用 ID,使其指向刚替换的可执行文件。 使用通过处理监视器获得的知识,还可以设置工作目录,并使用文件重定向修正将读取/写入操作重定向到包相对 "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.js指南:

Array key
applications id 使用 Id Application 包清单中元素的属性的值。
applications 可执行文件 要启动的可执行文件的包相对路径。 在大多数情况下,你可以在修改之前从包清单文件中获取此值。 它是元素的属性的值 Executable Application
applications workingDirectory (可选) 要用作启动的应用程序的工作目录的包相对路径。 如果未设置此值,操作系统将使用 System32 目录作为应用程序的工作目录。
进程 可执行文件 在大多数情况下,这将是上面配置的名称, executable 其中包含已删除的路径和文件扩展名。
修正 dll 要加载的链接地址的包相对路径,.msix/.appx。
修正 config (可选) 控制修正 dll 的行为方式。 此值的准确格式因修正链接而异,因为每个修正都可以根据需要解释此 "blob"。

applicationsprocessesfixups 键是数组。 这意味着,可以使用文件 config.js来指定多个应用程序、进程和修复 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.js上的,然后打包并安装应用程序。

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

默认情况下,跟踪修正将筛选出可能被视为 "预期" 的失败。 例如,应用程序可能会尝试无条件删除文件,而不检查其是否已存在,并忽略结果。 这会导致某些意外的失败被筛选掉,因此,在上述示例中,我们选择接收来自文件系统功能的所有失败。 我们这样做的原因是,在尝试从 Config.txt 文件中读取消息失败,并显示消息 "找不到文件"。 这是一种通常会出现的错误,通常不会被认为是意外情况。 在实践中,可能最好仅开始筛选意外故障,然后在出现仍无法确定的问题时回退到所有失败。

默认情况下,跟踪修正的输出将发送到附加的调试器。 在此示例中,我们不会附加调试器,而是使用 SysInternals 中的 DebugView 程序来查看其输出。 运行应用程序后,可以看到与之前相同的故障,这会使我们向相同的运行时修复。

找不到 TraceShim 文件

拒绝访问 TraceShim

调试、扩展或创建运行时修补程序

您可以使用 Visual Studio 来调试运行时修补程序、扩展运行时修补程序或从头开始创建一个修补程序。 你需要执行这些操作才能成功。

  • 添加打包项目
  • 为运行时修复添加项目
  • 添加启动 PSF Launcher 可执行文件的项目
  • 配置打包项目

完成后,解决方案将如下所示。

已完成解决方案

让我们看看此示例中的每个项目。

Project 用途
DesktopApplicationPackage 此项目基于Windows 应用程序打包项目,并输出 .msix 包。
Runtimefix 这是一个 c + + Dynamic-Linked 库项目,其中包含一个或多个用作运行时修复的替换函数。
PSFLauncher 这是 c + + 空 Project。 此项目是用于收集包支持框架的运行时可分发文件的位置。 它输出可执行文件。 该可执行文件是在启动解决方案时运行的第一件事。
WinFormsDesktopApplication 此项目包含桌面应用程序的源代码。

若要查看包含所有这些类型项目的完整示例,请参阅 PSFSample

让我们逐步完成在解决方案中创建和配置这些项目的步骤。

创建包解决方案

如果还没有适用于桌面应用程序的解决方案,请在 Visual Studio 中创建一个新的 空白解决方案

空白解决方案

你可能还需要添加任何应用程序项目。

添加打包项目

如果还没有 Windows 应用程序打包 Project,请创建一个,并将其添加到解决方案中。

包项目模板

有关 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) 项目添加到解决方案。

运行时修复库

右键单击该项目,然后选择"属性 "。

在属性页中,找到 "C++ 语言标准"字段,然后在该字段旁边的下拉列表中,选择 ISO C++17 Standard (/std:c++17) 选项。

ISO 17 选项

右键单击该项目,然后在上下文菜单中选择"管理 Nuget 包" 选项。 确保"包源" 选项设置为"全部 " 或"nuget.org"。

单击该字段旁边的设置图标。

搜索 PSF* Nuget 包,然后针对此项目安装它。

NuGet 包

如果要调试或扩展现有的运行时修补程序,请添加使用本指南的查找运行时修复部分中所述的指南获取的运行时修复文件。

如果打算创建全新的修补程序,请不要向此项目添加任何内容。 在本指南的稍后部分,我们将帮助你将正确的文件添加到此项目。 现在,我们将继续设置解决方案。

添加一个项目,该项目启动 PSF Launcher可执行文件

将 C++空Project 添加到解决方案。

空项目

使用上一部分所述的相同指南将 PSF Nuget 包添加到此项目。

打开项目的属性页,在"常规设置"页中,将"目标名称"属性设置为 或 PSFLauncher32 ,具体取决于 PSFLauncher64 应用程序的体系结构。

PSF Launcher参考

在解决方案中添加对运行时修复项目的项目引用。

运行时修复参考

右键单击引用,然后在"属性 " 窗口中应用这些值。

属性
复制本地 正确
复制本地附属程序集 正确
引用程序集输出 正确
链接库依赖项 错误
链接库依赖项输入 错误

配置打包项目

在打包项目中,右键单击 应用程序 文件夹,然后选择 添加引用

添加项目引用

选择 PSF Launcher和桌面应用程序项目,然后选择"确定 " 按钮。

桌面项目

备注

如果没有应用程序的源代码,只需选择 PSF Launcher项目。 我们将展示创建配置文件时如何引用可执行文件。

在"应用程序" 节点中,右键单击 PSF Launcher应用程序,然后选择"设置为 入口点"。

设置入口点

将名为 的文件添加到打包项目,然后将以下 json 文本复制 config.json 并粘贴到文件中。 将"包操作" 属性设置为 "内容"。

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

提供每个键的值。 使用此表作为指南。

Array key
applications id 使用包清单中 Id Application 元素的 属性的值。
applications 可执行文件 要启动的可执行文件的包相对路径。 在大多数情况下,可以在修改包清单文件之前获取此值。 它是 元素 Executable 的 属性的值 Application
applications workingDirectory (可选) 用作启动的应用程序的工作目录的包相对路径。 如果未设置此值,操作系统将使用 目录作为 System32 应用程序的工作目录。
进程 可执行文件 在大多数情况下,这将是上面配置的 的名称,其中删除了 executable 路径和文件扩展名。
修复 dll 要加载的修复 DLL 的包相对路径。
修复 config (可选) 控制修复 DLL 的行为方式。 此值的确切格式因修复而异,因为每个修复都可以根据需要解释此"blob"。

完成后,文件 config.json 将如下所示。

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

备注

applications processesfixups 键是数组。 这意味着,可以使用 config.json 文件指定多个应用程序、进程和修复 DLL。

调试运行时修复

在Visual Studio,按 F5 启动调试器。 首先,PSF 应用程序Launcher启动目标桌面应用程序。 若要调试目标桌面应用程序,必须手动附加到桌面应用程序进程,方法为选择"调试>附加到进程", 然后选择应用程序进程。 若要允许使用本机运行时修复 DLL 调试 .NET 应用程序,请选择托管和本机代码类型 (混合模式调试) 。

完成设置后,可以在桌面应用程序代码和运行时修复项目中的代码行旁边设置断点。 如果没有应用程序的源代码,则只能在运行时修复项目中的代码行旁边设置断点。

由于 F5 调试通过从包布局文件夹路径部署松散文件来运行应用程序,而不是从 .msix/.appx 包安装,因此布局文件夹通常与已安装的包文件夹没有相同的安全限制。 因此,在应用运行时修补程序之前,可能无法重现包路径访问拒绝错误。

若要解决此问题,请使用 .msix/.appx 包部署,而不是 F5 宽松文件部署。 若要创建 .msix/.appx 包文件,请使用 Windows SDK 中的MakeAppx实用工具,如上所述。 或者,在Visual Studio中,右键单击应用程序项目节点,然后选择"存储 "->"创建应用包"。

另一Visual Studio一个问题是,它不支持附加到调试器启动的任何子进程。 这使得难以在目标应用程序的启动路径中调试逻辑,启动后,必须手动Visual Studio逻辑。

若要解决此问题,请使用支持子进程附加的调试器。 请注意,通常无法将实时 JIT (调试) 附加到目标应用程序。 这是因为大多数 JIT 技术都涉及通过 ImageFileExecutionOptions 注册表项启动调试器以使用目标应用。 这会与用户用于将PSFLauncher.exe应用FixupRuntime.dll的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 技术社区网站上的 " 包支持框架 " 会话空间询问我们。