通过使用外部位置手动进行打包来授予包标识

有关添加包标识背后的动机,以及在 Visual Studio 中生成标识包和手动生成标识包之间的差异,请参阅 概述

本主题介绍如何手动生成和注册标识包。 有关在 Visual Studio 中创建标识包的信息,请参阅 在 Visual Studio 中使用外部位置打包以赋予包标识

以下是手动生成和注册标识包的步骤(本主题详细介绍):

  1. 为标识包创建包清单
  2. 生成并签名标识包
  3. 将标识元数据添加到桌面应用程序清单
  4. 在安装程序中注册标识包
  5. 可选步骤

为标识包创建程序包清单

创建标识包的第一步是基于以下模板创建包清单。 这是 MSIX 清单,但仅用于标识,不会更改应用的运行时行为。

<?xml version="1.0" encoding="utf-8"?>
<Package IgnorableNamespaces="uap uap10"
  xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
  xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
  xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
  xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities">
  <Identity Name="ContosoPhotoStore" Publisher="CN=Contoso" Version="1.0.0.0" ProcessorArchitecture="neutral" />
  <Properties>
    <DisplayName>Contoso PhotoStore</DisplayName>
    <PublisherDisplayName>Contoso</PublisherDisplayName>
    <Logo>Assets\storelogo.png</Logo>
    <uap10:AllowExternalContent>true</uap10:AllowExternalContent>
  </Properties>
  <Resources>
    <Resource Language="en-us" />
  </Resources>
  <Dependencies>
    <TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.19041.0" MaxVersionTested="10.0.26100.0" />
  </Dependencies>
  <Capabilities>
    <rescap:Capability Name="runFullTrust" />
    <rescap:Capability Name="unvirtualizedResources"/>
  </Capabilities>
  <Applications>
    <Application Id="ContosoPhotoStore" Executable="ContosoPhotoStore.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App"> 
      <uap:VisualElements AppListEntry="none" DisplayName="Contoso PhotoStore" Description="Contoso PhotoStore App" BackgroundColor="transparent" Square150x150Logo="Assets\Square150x150Logo.png" Square44x44Logo="Assets\Square44x44Logo.png" />
    </Application>
  </Applications>
</Package>

请注意以下有关此清单的重要详细信息:

  • 填入应用程序的详细信息到 Identity 元素属性中
    • Name 是标识包的所需名称
    • Publisher必须与用于对应用程序进行签名的证书匹配Subject
    • Version 是标识包的所需版本。 一种常见做法是将标识包版本与应用程序版本保持一致。 如果标识包的某个版本已经注册,则无法在系统上再次注册该版本。 必须先注销现有包,才能重新安装具有相同版本的包。
    • ProcessorArchitectureneutral 如下所示,以便标识包适用于所有体系结构(x86、x64 和 ARM64)
  • 将应用程序的详细信息输入到 DisplayNamePublisherDisplayName 元素中
    • 除非在简单标识之外向清单添加其他功能,否则这些值不会显示在任何位置
  • Logo 元素更新为应用程序安装目录中的相对路径,该路径将指向 .png、.jpg或 .jpeg 图像
  • 确保元素 AllowExternalContent 设置为 true 如下所示,以便重用现有安装程序
  • 按如下方式设置 TargetDeviceFamilyMinVersionMaxVersionTested
    • MinVersion设置为10.0.19041.0以在 Windows 10 和 Windows 11 操作系统版本中实现最大覆盖范围和统一性。
    • MinVersion设置为10.0.26100.0以将标识包限制为 Windows 11 版本 24H2 及更高版本
    • MaxVersionTested设置为10.0.26100.0,如下所示
    • 注意: AllowExternalContent 此处使用的功能是在 Windows 内部版本 10.0.19041.0 中引入的。 如果应用程序比这更低级别运行,则应在安装程序中执行 OS 版本检查,而不是在低于 10.0.19041.0 的 OS 版本上注册标识包。 请参阅 在安装程序中注册标识包
  • 确保按如下所示来声明 runFullTrustunvirtualizedResources 功能,以便实现 Win32 兼容性
  • 为与应用程序关联的每个可执行文件添加一个 Application 元素,如下所示
    • 确保 TrustLevelmediumIL,且 RuntimeBehaviorwin32App,如 Win32 兼容性所示。
  • VisualElements 子元素是必需的,但 AppListEntry="none" 属性可确保标识包不会显示在已安装的应用程序中。
    • 更新DisplayNameDescription属性的相关详细信息,其余属性保持原样(引用的图像路径不需要解析)
    • 有关此处可能需要本地化和图像的方案,请参阅 本地化和视觉资产

在后续步骤中注册包时,从此清单创建的标识包将连接到应用程序的安装目录。

生成并签名标识包

创建标识包清单后,使用 Windows SDK 中的 MakeAppx.exe 工具 生成标识包。

MakeAppx.exe pack /o /d <path to directory that contains manifest> /nv /p <output path>\MyPackage.msix

注意:需要 /nv 标志才能绕过清单中引用的文件路径的验证。

若要在最终用户计算机上安装,必须使用目标计算机上受信任的证书对标识包进行签名。 可以为 开发目的创建新的自签名证书 ,并使用 Windows SDK 中提供的 SignTool 对标识包进行签名,但 IT 部门或 Azure 受信任的签名 等服务的生产证书需要在最终用户计算机上注册该包。

SignTool.exe sign /fd SHA256 /a /f <path to certificate>\MyCertificate.pfx /p <certificate password> <path to package with external location>\MyPackage.msix

注意:有关如何使用生产证书在 CI/CD 管道中生成和签名标识包,请参阅 MSIX 和 CI/CD 管道概述 ,获取示例。

将标识元数据添加到桌面应用程序清单

通过包含应用程序清单(也称为并行清单或融合清单)并将元数据与标识包清单中的元数据匹配,你可以将标识包与应用程序可执行文件连接起来。

在 Visual Studio 中,可以通过打开“项目”上下文菜单并选择“添加新>>”,将应用程序清单添加到可执行项目。

下面是一个应用程序清单示例片段,其中演示了将二进制文件与标识包中的元数据连接所需的 msix 元素。

<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
  <assemblyIdentity version="0.0.0.0" name="ContosoPhotoStore"/>
  <msix xmlns="urn:schemas-microsoft-com:msix.v1"
          publisher="CN=Contoso"
          packageName="ContosoPhotoStore"
          applicationId="ContosoPhotoStore"
        />
</assembly>

元素的属性 msix 必须与标识包清单中的这些值匹配:

  • packageNamepublisher属性必须分别匹配您标识包清单中的Name元素中的PublisherIdentity属性。
  • applicationId属性必须与标识包清单中相应Id元素的Application属性匹配

在安装程序中注册标识包

将标识与应用程序关联的最后一步是在安装程序中注册标识包,并将其与应用程序的安装目录相关联。

PowerShell

使用正确的参数执行 powershell.exe 是注册包的最简单方法。 每用户安装与整机安装的指南有所不同。

按用户 (PowerShell)

在按用户安装过程中注册标识包:

powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command "Add-AppxPackage -Path <PackagePath> -ExternalLocation <ExternalLocation>"
  • 请将 <PackagePath> 设置为上一步生成的已签名标识包的绝对路径(包括文件名)。
  • <ExternalLocation> 设置为您的应用程序安装目录的绝对路径(不包括任何可执行文件名称)。

在按用户卸载过程中注销标识包:

powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command "Get-AppxPackage <PackageName> | Remove-AppxPackage"
  • <PackageName> 设置为你在标识包清单中定义的包名称(Identity 元素的 Name 特性)

按计算机 (PowerShell)

若要在计算机范围安装时注册标识包,请执行以下步骤:

powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command "Add-AppxPackage -Stage <PackagePath> -ExternalLocation <ExternalLocation>; Add-AppxProvisionedPackage -Online -PackagePath <PackagePath>"
  • <PackagePath> 设置为上一步生成的已签名标识包(包括文件名)的绝对路径。
  • <ExternalLocation> 设置为您的应用程序安装目录的绝对路径(不包括任何可执行文件名称)。

若要在计算机范围内卸载期间注销标识包,请执行以下操作:

powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -Command "$packages = Get-AppxPackage <PackageName>; foreach ($package in $packages) { Remove-AppxProvisionedPackage -PackageName $package.PackageFullName -Online }; foreach ($package in $packages) { Remove-AppxPackage -Package $package.PackageFullName -AllUsers }
  • <PackageName> 设置为你在标识包清单中定义的包名称(Identity 元素的 Name 特性)

PackageManager API

如果想要调用 OS API 来注册和注销标识包,PackageManager API 提供与 PowerShell 等效的功能。 对于每个用户安装和计算机范围安装,指导方针有所不同。

下面是演示 API 的代码片段。 有关 C# 和 C++中的生产就绪代码,请参阅 示例应用

按用户 (PackageManager)

下面的代码列表演示如何使用 AddPackageByUriAsync 方法注册标识包,并使用 RemovePackageAsync 方法取消注册标识包。

using Windows.Management.Deployment;

...

// Register the identity package during install

var externalUri = new Uri(externalLocation);
var packageUri = new Uri(packagePath);

var packageManager = new PackageManager();

var options = new AddPackageOptions();
options.ExternalLocationUri = externalUri;

await packageManager.AddPackageByUriAsync(packageUri, options);

...

// Unregister the identity package during uninstall

var packageManager = new PackageManager();
var packages = packageManager.FindPackagesForUserWithPackageTypes("", "<IdentityPackageFamilyName>", PackageType.Main);
foreach (var package in packages)
{
  await packageManager.RemovePackageAsync(package.Id.FamilyName);
}

请注意以下有关此代码的重要详细信息:

  • externalLocation 设置为您的应用程序安装目录的绝对路径(不包括任何可执行文件名称)
  • packagePath 设置为上一步中生成的已签名标识包的绝对路径以及文件名
  • 在注册标识包的系统上运行 PowerShell 命令 Get-AppxPackage <IdentityPackageName> 即可找到 <IdentityPackageFamilyName>。 该 PackageFamilyName 属性包含此处要使用的值。

按计算机 (PackageManager)

下面的代码列表演示如何使用 StagePackageByUriAsyncProvisionPackageForAllUsersAsync 方法注册标识包,并使用 DeprovisionPackageForAllUsersAsyncRemovePackageAsync 方法取消注册标识包。

// Register the identity package during install

var externalUri = new Uri(externalLocation);
var packageUri = new Uri(packagePath);

var packageManager = new PackageManager();

var options = new StagePackageOptions();
options.ExternalLocationUri = externalUri;

await packageManager.StagePackageByUriAsync(packageUri, options);
await packageManager.ProvisionPackageForAllUsersAsync(packageFamilyName);

...

// Unregister the identity package during uninstall

var packageManager = new PackageManager();

var packages = packageManager.FindPackagesForUserWithPackageTypes("", "<IdentityPackageFamilyName>", PackageType.Main);
foreach (var package in packages)
{
  await packageManager.DeprovisionPackageForAllUsersAsync(package.Id.FamilyName);
  await packageManager.RemovePackageAsync(package.Id.FamilyName, RemovalOptions.RemoveForAllUsers);
}

请注意以下有关此代码的重要详细信息:

  • externalLocation 设置为您的应用程序安装目录的绝对路径(不包括任何可执行文件名称)
  • packagePath 设置为上一步中生成的已签名身份包的绝对路径(包括文件名)
  • 在注册标识包的系统上运行 PowerShell 命令 Get-AppxPackage <IdentityPackageName> 即可找到 <IdentityPackageFamilyName>。 该 PackageFamilyName 属性包含此处要使用的值。

示例应用

请参阅 PackageWithExternalLocation 示例,了解功能齐全的 C# 和C++应用,这些应用演示如何注册和注销标识包。

可选步骤

本地化和视觉资产

了解包标识的某些功能可能会导致标识包清单中的字符串和映像显示在 Windows OS 中。 例如:

  • 使用相机、麦克风或位置 API 的应用程序将在 Windows 隐私设置中具有专用的控制切换,以及用户可用于授予或拒绝对这些敏感资源的访问权限的中转同意提示。
  • 注册共享目标的应用程序将显示在共享对话框中。

若要本地化标识包清单中的字符串,请参阅 本地化清单

在身份包清单中的 VisualElements 属性中提供图像路径时,所提供的路径应为应用程序安装目录中的相对路径,这些路径将解析为 .png、.jpg或 .jpeg 图像。 属性名称指示图像的预期尺寸(150x150 和 40x40)。