Windows 应用中的包标识概述

包标识 是跨空间和时间的唯一标识符。 正如 DNA 唯一标识你一样,包标识唯一地标识包。

包包含一组关联的组件(文件等)。 没有两个包具有相同的标识,并且对与包关联的比特的任何更改都需要一个不同的标识。

什么是包标识?

包标识是一个逻辑构造,唯一标识包。 身份有 5 个部分:

  • 名字: 这是应用开发人员选择的名称。 Microsoft应用商店在应用商店中的所有应用开发人员中强制实施所有应用名称的唯一性,但不能保证名称在常规生态系统中是唯一的。
  • 版本: 包的版本号。 应用开发人员可以选择任意版本号,但必须确保版本号随更新而增加。
  • 架构: 包面向的处理器架构。 同一应用可以针对不同的处理器体系结构生成,每个版本驻留在其自己的包中。
  • ResourceId: 应用开发人员选择的字符串,用于唯一标识资源包,例如不同的语言或不同的显示比例。 资源包通常与体系结构无关。 对于捆绑包,ResourceId 始终为 ~
  • 发布者: 应用开发人员的使用者名称,由其签名证书加以标识。 这在理论上对于每个应用开发人员来说都是唯一的,因为信誉良好的证书颁发机构使用唯一的真实名称和标识来填充证书的使用者名称字段。

此构造有时称为 5 部分元组

注释

未签名的包(1)仍需要有发布者,(2)发布者 必须包含未签名标记(OID.2.25.311729368913984317654407730594956997722=1),(3)未签名标记 必须是发布者 字符串中的最后一个字段,并且(4)未签名的包没有证书或签名。

包标识字段限制

领域 数据类型 限制 注释
名称 包字符串 最小值:3
最大值:50
验证 API 允许的值(请参阅 包字符串
版本 DotQuad 最小值:0.0.0.0
最大值:65535.65535.65535.65535
字符串格式使用十进制点表示法“Major.Minor.Build.Revision”
建筑 枚举 最小值:N/A
最大值:N/A
允许的值为“neutral”、“x86”、“x64”、“arm”、“arm64”、“x86a64”
资源标识符 包字符串 最小值:0
最大值:30
验证 API 允许的值(请参阅 包字符串
发布者 字符串 最小值:1
最大值:8192
根据 X.509 标准允许的值
发布者ID (PublisherId) 字符串 最小值:13
最大值:13
Base32 编码的 Crockford 变体,即 [a-hjkmnp-tv-z0-9]

什么是“包字符串”?

包字符串 是允许以下字符的字符串:

  • 允许的输入字符(ASCII 子集)
    • 大写字母 (U+0041 至 U+005A)
    • 小写字母(U+0061 到 U+007A)
    • 数字 (U+0030 到 U+0039)
    • 点 (U+002E)
    • 短划线 (U+002D)

禁止将以下值用作包字符串:

条件 禁止的值
不能等于 “.”、“..”、“con”、“prn”、“aux”、“nul”、“com1”、“com2”、“com3”、“com4”、“com5”、“com6”、“com7”、“com8”、“com9”、“lpt1”、“lpt2”、“lpt3”、“lpt4”、“lpt5”、“lpt6”、“lpt7”、“lpt8”、“lpt9”
不能以 “con.”、“prn.”、“aux.”、“nul.”、“com1.”、“com2.”、“com3.”、“com4.”、“com5.”、“com6.”、“com7.”、“com8.”、“com9.”、“lpt1.”、“lpt2.”、“lpt3.”、“lpt4.”、“lpt5.”、“lpt6.”、“lpt7.”、“lpt8.”、“lpt9.”、“xn--”
不能以...结束 "."
不能包含 “.xn--”

必须使用不区分大小写的字符串比较 API(例如_wcsicmp)来比较 包字符串

包标识和nameresourceid字段是包字符串。

PackageId 对象

PackageId 是一个对象,其中包含由 5 个部分构成的元组,各个部分作为独立的字段(NameVersionArchitectureResourceIdPublisher)。

软件包全名

包全名 是从包的身份标识(名称、版本、架构、资源标识、发布者)的五个部分派生而来的不透明字符串。

<Name>_<Version>_<Architecture>_<ResourceId>_<PublisherId>

例如,Windows 照片应用的一个程序包全名是“Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe”, 其中,“Microsoft.Windows.Photos”是名称,“2020.20090.1002.0”是版本号,“x64”是目标处理器体系结构,资源 ID 为空(最后两个下划线之间没有内容),而“8wekyb3d8bbwe”是Microsoft的发布者 ID。

包全名 唯一标识 MSIX 包或捆绑包。 如果两个包或捆绑包具有不同的内容,但具有相同的包全名,则这是一个错误。

注释

MSIX 是上一个术语 APPX的新名称。 有关详细信息,请参阅 什么是 MSIX?

软件包系列名称

包系列名称 是由包标识的两个组成部分中的 名称发布者派生出的不透明字符串:

<Name>_<PublisherId>

例如,Windows 照片应用的程序包系列名称是“Microsoft.Windows.Photos_8wekyb3d8bbwe”,其中“Microsoft.Windows.Photos”是名称,“8wekyb3d8bbwe”是Microsoft的发布者 ID。

“Package Family Name”通常被称为“没有版本号的包完整名称”。

注释

这个说法并不完全准确,因为 Package Family Name 也缺少体系结构和资源 ID。

注释

数据和安全性通常限定为包系列。 例如,如果将从记事本版本 1.0.0.0 包安装的记事本应用配置为启用 Wordwrap,则体验会很差。 记事本更新为 1.0.0.1 版本后,您的配置数据没有被迁移到新版本的软件包中。

发布者 ID

包系列名称是一个字符串,格式为:

<name>_<publisherid>

其中,发布者 ID 具有一些非常具体的属性:

  • 派生自发布者
  • MinLength = MaxLength = 13 个字符 [fixed-size]
  • 允许的字符 (作为正则表达式) = a-hj-km-np-tv-z0-9
    • Base-32,Crockford Variant,即字母数字(A-Z0-9),除了没有我(眼睛),L(ell),O(哦)或 U (you)
  • 比较时序号不区分大小写 --- ABCDEFABCDEFG == abcdefabcdefg

所以你永远不会看到 %: \ / “ ? 或发布者 ID 中的其他字符。

有关详细信息,请参阅 PackageFamilyNameFromIdPackageNameAndPublisherIdFromFamilyName

发布者 ID 通常称为 PublisherId。

为什么存在发布者 ID?

发布服务器 ID 存在,因为发布服务器需要与证书的 X.509 名称/签名者匹配,因此:

  • 它可能非常大(长度 <= 8192 字符)
  • 它可以包括不常见或受限的字符(例如反斜杠等)

这些问题会使某些 X.509 字符串尴尬或无法在文件系统、注册表、URL 和其他上下文中使用。

如何创建 PublisherId?

使用 PackageNameAndPublisherIdFromFamilyName 来从 PublisherId提取 PackageFamilyName

使用 PackageIdFromFullNamePublisherId中提取 PackageFullName

很少需要从PublisherId创建Publisher,但可以使用可用的 API 来实现这一点。

#include <appmodel.h>

HRESULT PublisherIdFromPublisher(
    _In_ PCWSTR publisher,
    _Out_writes_(PACKAGE_PUBLISHERID_MAX_LENGTH + 1) PWSTR publisherId)
{
    PCWSTR name{ L"xyz" };
    const size_t nameLength{ ARRAYSIZE(L"xyz") - 1 };
    const size_t offsetToPublisherId{ name + 1 }; // xyz_...publisherid...
    PACKAGE_ID id{};
    id.name = name;
    id.publisher = publisher;
 
    WCHAR familyName[PACKAGE_PUBLISHERID_MAX_LENGTH + 1]{};
    UINT32 n{ ARRAYSIZE(familyName) };
    RETURN_IF_WIN32_ERROR(PackageFamilyNameFromId(&id, &n, familyName);
    RETURN_IF_FAILED(StringCchCopyW(publisherId, PACKAGE_PUBLISHERID_MAX_LENGTH + 1, familyName + offsetToPublisherId));
    return S_OK;
}

下面是相同操作的经典 Windows C 的实现:

#include <appmodel.h>

HRESULT PublisherIdFromPublisher(
    _In_ PCWSTR publisher,
    _Out_writes_(PACKAGE_PUBLISHERID_MAX_LENGTH + 1) PWSTR publisherId)
{
    const WCHAR c_name[]{ L"xyz" };
    const UINT32 c_nameLength{ ARRAYSIZE(c_nameForPublisherToPublisherId) - 1 };

    PACKAGE_ID id{};
    id.name = c_name;
    id.publisher = publisher;
    WCHAR familyName[PACKAGE_PUBLISHERID_MAX_LENGTH + 1]{};
    UINT32 n{ ARRAYSIZE(familyName) };
    RETURN_IF_WIN32_ERROR(PackageFamilyNameFromId(&id, &n, familyName));
    RETURN_IF_FAILED(StringCchCopyW(publisherId, PACKAGE_PUBLISHERID_MAX_LENGTH + 1,  familyName + c_nameLength + 1);
    return S_OK;
}

通过将包 ID 转换为格式为 xyz_<publisherid>的包系列名称,来创建 PublisherId。 这个食谱是稳定可靠的。

这只需要使用 SDK 中的 appmodel.h 进行编译,并链接 kernel32.lib(如果使用 APIsets,还可以选择链接 kernelbase.lib、onecore.lib 或 api-ms-win-appmodel-runtime-l1.lib)。

了解包标识中的处理器体系结构

常见的误解是, Architecture=x64 这意味着包只能包含 x64 代码。 这不是真的。 这意味着该包适用于支持 x64 代码的系统,并且可由 x64 应用使用。 你可以创建一个仅包含 PDF 文件的包,但声明它 <Identity Architecture=x64...> ,因为它仅用于安装在 x64 兼容的系统上(例如 x64 包只能安装在 x64 和(从 Windows 11 起)Arm64 系统,因为 x86、Arm 和 Windows 10 Arm64 系统不支持 x64。

更大的误解是,Architecture=neutral并不 意味着包中没有可执行代码。 这意味着包适用于所有体系结构。 例如,你可以创建一个包含用 JavaScript、Python、C# 等语言编写的 AES 加密 API 的包,但其在 Arm64 系统上的表现不令人满意。 因此,你包括优化的 Arm64 二进制文件并实现 API 来处理它:

void Encrypt(...)
{
    HANDLE h{};
    if (GetCpu() == arm64)
    {
        h = LoadLibrary(GetCurrentPackagePath() + "\bin\encrypt-arm64.dll")
        p = GetProcAddress(h, "Encrypt")
        return (*p)(...)
    }
    else
    {
        // ...call other implementation...
    }
}

或者,可以创建具有多个变体的中性包:

\
    bin\
        encrypt-x86.dll
        encrypt-x64.dll
        encrypt-arm.dll
        encrypt-arm64.dll

然后,开发人员可以在运行时通过执行 LoadLibrary("bin\encrypt-" + cpu + ".dll") 来获取其进程的相应二进制文件。

通常,中性包不含特定架构的内容,但也可能有例外。 人们能做到的事情是有限的(例如,你可以创建一个记事本程序包,其中包含为 x86 + x64 + arm + arm64 编译的 notepad.exe,但 appxmanifest.xml 只能声明指向其中一个的 <Application Executable=...>)。 鉴于有捆绑包允许你仅安装所需的组件,这种做法是非常不常见的。 这不是非法的,只是先进和奇异。

此外, Architecture=x86 (或 x64|arm|arm64)并不意味着包仅包含指定体系结构的可执行代码。 这只是极其常见的情况。

注释

在此上下文中讨论“代码”或“可执行代码”时,我们指的是可移植可执行文件(PE)文件。

包标识是否区分大小写?

大多数情况下,不,但 Publisher 区分大小写。

其余字段(NameResourceIdPublisherIdPackageFullNamePackageFamilyName)不是。 这些字段会保留大小写,但比较时不区分大小写。

另请参阅

包标识

PackageFamilyNameFromId

PackageNameAndPublisherIdFromFamilyName