了解打包的桌面应用如何在 Windows 上运行

本主题介绍可以为 Windows 应用包创建的桌面应用程序的类型,以及有必要了解的一些操作系统 (OS) 行为和其他规范。 我们将会深入了解以下各项(介绍的规范行为取决于你的应用类型):

桌面应用程序类型

可以创建和打包两种类型的桌面应用程序。 通过使用应用元素的 uap10:RuntimeBehavior 属性,在其应用包清单中声明应用的类型:

  • 一种类型同时包括 WinUI 3 应用(使用 Windows 应用 SDK)和桌面桥应用 (Centennial)。 使用 uap10:RuntimeBehavior="packagedClassicApp" 声明。
  • 另一种类型表示其他类型的 Win32 应用,包括使用外部位置打包的应用。 使用 uap10:RuntimeBehavior="win32App" 声明。

通用 Windows 平台 (UWP) 应用 (uap10:RuntimeBehavior="windowsApp") 也属于打包应用;但不在此主题的讨论范围内。

然后(相同应用元素的) uap10:TrustLevel 属性决定打包应用的进程是否在应用容器内部运行。

  • 完全信任应用。 使用 uap10:TrustLevel="mediumIL" 声明。
  • appContainer 应用。 使用 uap10:TrustLevel="appContainer" 声明。 在轻型应用容器中运行(因此,使用文件系统和注册表虚拟化进行隔离)。 有关详细信息,请参阅 MSIX appContainer 应用

重要

有关详细信息、依赖项和功能需求,请参阅应用中这两个属性的文档。 另请参阅 Windows 10,版本 2004(10.0;内部版本 19041)中引入了 uap10

打包和应用容器的目的

打包应用的目的是在运行时为其授予程序包标识符。 某些 Windows 功能需要程序包标识符,(请参阅需要程序包标识符的功能)。 可以打包上述应用类型的所有组合(并因此从程序包标识符获益)。

AppContainer 应用的主要目标是尽可能多地将应用状态与系统状态分开,同时保持与其他应用的兼容性。 Windows 通过检测和重定向它在运行时对文件系统和注册表所做的某些更改(称为虚拟化)来完成此目标。 当某个部分仅适用于虚拟化应用时,我们会给出说明。

安装

应用包按用户安装,而不是在整个系统中安装。 新计算机上新包的默认位置在 C:\Program Files\WindowsApps\<package_full_name> 下,可执行文件名为“app_name.exe”。 但可将包安装到其他地点,例如,Visual Studio 的“开始”命令使用项目的 $(OutDir)

部署后,软件包文件由操作系统标记为只读并严格锁定。 如果这些文件遭到篡改,Windows 将阻止应用启动。

位置 C:\Program Files\WindowsApps 即是 PackageVolume。 该位置是 Windows 交付的默认 PackageVolume;不过可以在任意驱动器上的任意路径下创建 PackageVolume。 此外,并非所有包都安装在 PackageVolume 中(请参阅上面的 Visual Studio 示例)。

文件系统

操作系统根据文件夹位置的不同,支持对打包桌面应用程序执行不同级别的文件系统操作。

已针对设备进行优化

为了避免文件重复来优化磁盘存储空间,并减少下载文件时所需的带宽,OS 利用文件的单个存储和硬链接。 当用户下载 MSIX 包时,使用 AppxManifest.xml 来确定早期包安装的磁盘上是否已存在包中包含的数据。 如果多个 MSIX 包中存在相同的文件,则 OS 仅在磁盘上存储一次共享文件,并创建从这两个包到该共享文件的硬链接。 文件是以 64kb 块的形式下载的,因此即使磁盘上存在正在下载的文件的部分内容,也仅下载内容不同的增量。 这会减少下载所用的带宽。

Windows 10 版本 1903 和更高版本上的 AppData 操作

本部分仅适用于虚拟化应用。

用户的“AppData”文件夹中所有新建的文件和文件夹(例如,C:\Users\<user_name>\AppData)都将写入特定于用户和应用的专用位置,但在运行时将会合并,以在实际的 AppData 位置显示。 对于仅由应用程序本身使用的项目,这可以实现某种程度的状态隔离,同时,在卸载应用程序后,使系统能够清理这些文件。

允许修改 AppData 用户文件夹中的现有文件,以在应用程序与 OS 之间提供更高程度的兼容性和交互性。 这可以减轻文件系统的“腐朽”程度,因为 OS 能够意识到应用程序所做的每项文件或目录更改。 状态隔离还使打包的桌面应用程序能够选取同一应用程序的非打包版本的移除位置。 请注意,OS 不支持将虚拟文件系统 (VFS) 文件夹用作用户的 AppData 文件夹。

比 Windows 10 版本 1903 更低的版本上的 AppData 操作

本部分仅适用于虚拟化应用。

对用户 AppData 文件夹(例如 C:\Users\<user_name>\AppData)的所有写入(包括创建、删除和更新)将在写入时复制到特定于用户和应用的专用位置。 这会造成打包应用程序正在编辑真正的 AppData 的假象,而实际上它是在修改一个专用副本。 通过以这种方式重定向写入,系统可以跟踪应用所作的所有文件修改。 这样,在卸载应用程序后,系统就可以清理这些文件,从而减轻系统的“腐朽”程度,并为用户提供更好的应用程序删除体验。

工作目录和应用程序文件

本部分仅适用于虚拟化应用。

除了重定向 AppData 以外,Windows 的常用文件夹(System32Program Files (x86) 等)还与应用包中的相应目录动态合并。 每个包都在其根目录中包含一个名为“VFS”的文件夹。 目录 VFS 中的目录或文件的任何读取都会在运行时与其在本机的对应项合并。 例如,应用可能包含 C:\Program Files\WindowsApps\<package_full_name>\VFS\SystemX86\vc10.dll 作为其应用打包的一部分,但文件会显示为安装在 C:\Windows\System32\vc10.dll。 这保留了与可能预期文件处于非软件包位置的桌面应用程序的兼容性。

不允许对应用包中的文件/文件夹执行写入。 OS 将忽略对不在包中的文件和文件夹的写入,只要用户拥有权限,便允许此类操作。

常见文件系统操作

以下简短参考表格显示了常见文件系统操作以及 OS 如何处理它们。

操作 结果 示例
读取或枚举已知的 Windows 文件或文件夹 C:\Program Files\<package_full_name>\VFS\<well_known_folder> 与本地系统对应项的动态合并。 读取 C:\Windows\System32 会返回 C:\Windows\System32 的对应项以及 C:\Program Files\WindowsApps\<package_full_name>\VFS\SystemX86 的对应项。
AppData 下写入 Windows 10 1903 版及更高版本:在以下目录中创建的新文件和文件夹将重定向到特定于用户和包的专用位置:
  • Local
  • Local\Microsoft
  • 漫游
  • Roaming\Microsoft
  • Roaming\Microsoft\Windows\Start Menu\Programs
在响应文件打开命令时,OS 将先从特定于用户和包的位置打开文件。 如果不存在此位置,则 OS 将尝试从真实的 AppData 位置打开文件。 如果已从真实的 AppData 位置打开该文件,则不会对该文件进行虚拟化。 如果用户拥有权限,则允许删除 AppData 下的文件。

比 Windows 10 版本 1903 更早的版本:在写入时复制到每用户、每应用位置。

AppData 通常为 C:\Users\<user_name>\AppData
在包内写入 不允许。 该包是只读的。 不允许在 C:\Program Files\WindowsApps\<package_full_name> 下写入。
在包外部写入 如果用户具有权限则允许。 如果包不包含 C:\Program Files\WindowsApps\<package_full_name>\VFS\SystemX86\foo.dll 且用户具有权限,则允许写入到 C:\Windows\System32\foo.dll

打包的 VFS 位置

本部分仅适用于虚拟化应用。

此表显示了作为包的一部分的文件交付在应用的系统上重叠。 应用程序将认为这些文件位于所列的系统位置,但这些文件实际上位于 C:\Program Files\WindowsApps\<package_full_name>\VFS 中的重定向位置。 FOLDERID 位置来自于 KNOWNFOLDERID 常数。

系统位置 重定向位置(在 [<package_root>]\VFS 下) 在体系结构上有效
FOLDERID_SystemX86 SystemX86 x86, amd64
FOLDERID_System SystemX64 amd64
FOLDERID_ProgramFilesX86 ProgramFilesX86 x86, amd6
FOLDERID_ProgramFilesX64 ProgramFilesX64 amd64
FOLDERID_ProgramFilesCommonX86 ProgramFilesCommonX86 x86, amd64
FOLDERID_ProgramFilesCommonX64 ProgramFilesCommonX64 amd64
FOLDERID_Windows Windows x86, amd64
FOLDERID_ProgramData 通用 AppData x86, amd64
FOLDERID_System\catroot AppVSystem32Catroot x86, amd64
FOLDERID_System\catroot2 AppVSystem32Catroot2 x86, amd64
FOLDERID_System\drivers\etc AppVSystem32DriversEtc x86, amd64
FOLDERID_System\driverstore AppVSystem32Driverstore x86, amd64
FOLDERID_System\logfiles AppVSystem32Logfiles x86, amd64
FOLDERID_System\spool AppVSystem32Spool x86, amd64

注册表

本部分(及其子部分)仅适用于虚拟化应用。

应用包中有一个 registry.dat 文件,该文件充当真实注册表中的 HKLM\Software 的逻辑(虚拟)等效项。 在运行时,此虚拟注册表将此配置单元的内容合并到本机系统配置单元中,以提供两者的单一视图。 例如,如果 registry.dat 包含单个密钥“Foo”,则在运行时还会显示 HKLM\Software 的读取以包含 Foo(除了所有本机系统密钥以外)。

尽管 MSIX 包包含 HKLMHKCU 密钥,但它们的处理方式不同。 只有 HKLM\Software 下的密钥属于包的一部分;HKCU 下的密钥或注册表的其他部分不属于。 不允许写入包中的密钥或值。 只要用户拥有权限,便允许对不在包中的项或值执行写入。

HKCU 下的所有写入都在写入时复制到特定于用户和应用的专用位置。 过去,卸载程序无法清理 HKEY_CURRENT_USER,因为已注销用户的注册表数据已卸载并且不可用。

所有写入将在包升级期间保留,并且仅在完全删除应用程序时才删除这些写入内容。

常见注册表操作

本部分的大多数内容仅适用于虚拟化应用。

以下简短参考表格显示了常见注册表操作以及 OS 如何处理它们。

操作 结果 示例
读取或枚举 HKLM\Software 包配置单元与本地系统对应项的动态合并。 如果 registry.dat 包含单个密钥“Foo”,则在运行时 HKLM\Software 的读取会同时显示 HKLM\SoftwareHKLM\Software\Foo 的内容。
HKCU 下写入 在写入时复制到特定于用户和应用的专用位置。 文件与 AppData 相同。
在包内写入。 不允许。 该包是只读的。 如果包配置单元中存在相应的键/值,则不允许在 HKLM\Software 下写入。
在包外部写入 由 OS 忽略。 如果用户具有权限则允许。 只要包配置单元中不存在相应的键/值,并且用户具有正确的访问权限,便允许 HKLM\Software 下的写入。

卸载

本部分仅适用于虚拟化应用。

当用户卸载包时,位于 C:\Program Files\WindowsApps\<package_full_name> 下的所有文件和文件夹都会被删除,在打包过程中捕获的对 AppData 或注册表的所有重定向写入也会被删除。