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

本主题介绍了可以创建 Windows 应用包的桌面应用类型,以及一些操作系统(OS)行为和其他特别重要的细节。 我们将了解以下项的详细信息(如我们所示,具体行为取决于应用的类型):

桌面应用的类型

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

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

通用 Windows 平台(UWP)应用(uap10:RuntimeBehavior="windowsApp")也打包;但本主题不涉及它们。

然后 ,uap10:TrustLevel 属性(同一 Application 元素)确定打包应用的进程是否在应用容器中运行。

  • 完全信任应用。 用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)

部署后,包文件被标记为只读,并且被操作系统(OS)严格锁定。 如果这些文件被篡改,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 不支持用户的 AppData 文件夹的虚拟文件系统 (VFS) 文件夹。

早于 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 如何处理它们。

操作 结果 示例:
读取或枚举已知的 Windows 文件或文件夹 C:\Program Files\<package_full_name>\VFS\<well_known_folder> 与本地系统对应项动态合并。 读取 C:\Windows\System32 返回 C:\Windows\System32C:\Program Files\WindowsApps\<package_full_name>\VFS\SystemX86 的内容。
写入到 AppData Windows 10 版本 1903 及更高版本:在以下目录下创建的新文件和文件夹将重定向到每用户、每个包专用位置:
  • 本地
  • Local\Microsoft
  • 漫游
  • Roaming\Microsoft
  • 漫游\Microsoft\Windows\开始菜单\程序
为了响应文件打开命令,操作系统将首先从用户特定包位置打开该文件。 如果该位置不存在,则 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 包Hive与本地系统对应单元的动态合并。 如果 registry.dat 包含单个密钥 Foo,则在运行时读取 HKLM\Software 会显示 HKLM\SoftwareHKLM\Software\Foo 的内容。
HKCU下写入 在写入时复制到每用户、每应用专用位置。 AppData 文件相同。
在包内写入。 不允许。 包是只读的。 如果包配置单元中存在相应的键/值,则不允许在 HKLM\Software 下写入。
在包外部写入 被操作系统忽略。 如果用户具有权限,则允许。 只要包配置单元中不存在相应的键/值,并且用户具有正确的访问权限,就允许在 HKLM\Software 下写入。

卸载

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

当用户卸载软件包时,位于 C:\Program Files\WindowsApps\<package_full_name> 下的所有文件和文件夹将被删除,以及打包过程中捕获到的任何对 AppData 或注册表条目的重定向写入。