Windows 應用程式中套件識別資料的概觀
套件識別資料是跨空格和時間的唯一識別碼。 就像您的 DNA 可唯一識別您一樣,套件識別資料可唯一識別套件。
套件具有一組相關聯的位元 (檔案等)。 沒有兩個套件具有相同的識別資料,且任何與套件相關聯的位元變更都需要不同的識別資料。
什麼是套件識別資料?
套件識別資料是邏輯建構,可唯一識別套件。 識別資料有 5 個部分:
- 名稱:這是應用程式開發人員選擇的名稱。 Microsoft Store 會對市集內所有應用程式開發人員強制執行所有應用程式名稱的唯一性,但在一般生態系統中不保證名稱是唯一的。
- 版本:套件的版本號碼。 應用程式開發人員可以選擇任意版本號碼,但必須確保版本號碼隨著更新而增加。
- 架構:封裝的目標處理器架構。 相同的應用程式可以建置為以不同的處理器架構為目標,每個組建都位於自己的套件中。
- ResourceId:應用程式開發人員選擇的字串,可唯一識別資源套件,例如不同語言或不同的顯示縮放比例。 資源套件通常是架構中性套件。 對於套件組合,ResourceId 一律為
~
。 - 發行者:應用程式開發人員的主體名稱,由其簽署憑證所識別。 這在理論上對每個應用程式開發人員而言都是唯一的,因為信譽良好的憑證授權單位單位會使用唯一的實際名稱和身分識別來填入憑證的主體名稱欄位。
此建構有時稱為 5 部分 Tuple。
注意
未簽署的套件 (1) 仍然需要發行者、(2) 發行者必須包含未簽署標記 (OID.2.25.311729368913984317654407730594956997722=1)、(3) 未簽署標記必須是發行者字串中的最後一個欄位,且 [4] 沒有未簽署套件的憑證或簽署。
套件識別資料欄位限制
欄位 | 資料類型 | 限制 | 註解 |
---|---|---|---|
名稱 | 套件字串 | Min: 3 Max: 50 |
每個驗證 API 允許的值 (請參閱套件字串) |
版本 | DotQuad | Min: 0.0.0.0 Max: 65535.65535.65535.65535 |
字串表單使用 base-10 點標記法,「Major.Minor.Build.Revision」 |
架構 | 列舉型別 | Min: N/A Max: N/A |
允許的值為「neutral」、「x86」、「x64」、「arm」、「arm64」、「x86a64」 |
ResourceId | 套件字串 | Min: 0 Max: 30 |
每個驗證 API 允許的值 (請參閱套件字串) |
發行者 | String | Min: 1 Max: 8192 |
每個 X.509 允許的值 |
PublisherId | String | Min: 13 Max: 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)。
套件識別資料的 name
和 resourceid
欄位是套件字串。
PackageId 物件
PackageId 是物件,包含 5 部分 Tuple 作為個別欄位 (Name
、Version
、Architecture
、ResourceId
、Publisher
)。
套件完整名稱
套件完整名稱是衍生自套件識別資料所有 5 部分的不透明字串 (名稱、版本、架構、resourceid、發行者)
<Name>_<Version>_<Architecture>_<ResourceId>_<PublisherId>
例如,Windows 相片應用程式的一個套件完整名稱是「Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe」,其中「Microsoft.Windows.Photos」是名稱,「2020.20090.1002.0」是版本號碼,「x64」是目標處理器架構,資源識別碼是空的 (最後兩個底線之間沒有內容),而「8wekyb3d8bbwe」是 Microsoft 的發行者識別碼。
套件完整名稱可唯一識別 MSIX 套件或套件組合。 有兩個套件或套件組合具有不同內容,但具有相同套件完整名稱是錯誤的。
注意
MSIX 是上一個字詞 APPX 的新名稱。 如需相關資訊,請參閱什麼是 MSIX?
套件系列名稱
套件系列名稱是一個不透明的字串,衍生自套件識別資料的兩個部分 - 名稱和發行者:
<Name>_<PublisherId>
例如,Windows 相片應用程式的套件系列名稱是「Microsoft.Windows.Photos_8wekyb3d8bbwe」,其中「Microsoft.Windows.Photos」是名稱,而「8wekyb3d8bbwe」是 Microsoft 的發行者識別碼。
套件系列名稱通常稱為「無版本套件完整名稱」。
注意
這並不嚴格,因為套件系列名稱也缺少架構和資源識別碼。
注意
資料和安全性通常會限定於套件系列。 例如,如果您設定從記事本 1.0.0.0 版套件安裝的記事本應用程式來啟用 Wordwrap,將無法獲得良好體驗。 接著,記事本會更新為 1.0.0.1,且您的設定資料不會延續至較新版本的套件。
發行者識別碼
套件系列名稱是格式如下的字串:
<name>_<publisherid>
其中發行者識別碼具有一些非常特定的屬性:
- 衍生自發行者
- MinLength = MaxLength = 13 個字元 [固定大小]
- 允許的字元 (如 RegEx) = a-hj-km-np-tv-z0-9
- Base-32,Crockford 變數, 即英數位元 (A-Z0-9),除了沒有 I (eye)、L (ell)、O (oh) 或 U (you)
- 比較的序數不區分大小寫 --- ABCDEFABCDEFG == abcdefabcdefg
所以您永遠不會看到 % : \ / " ? 或發行者識別碼中的其他字元。
如需詳細資料,請參閱 PackageFamilyNameFromId 和 PackageNameAndPublisherIdFromFamilyName。
發行者識別碼通常稱為 PublisherId。
為什麼發行者識別碼存在?
發行者識別碼存在的原因在於 Publisher 必須符合憑證的 X.509 名稱/簽署者,因此:
- 其可以非常大 (長度 <= 8192 個字元)
- 其可以包含不便或受限制的字元 (反斜線等)
這些問題可能會讓某些 X.509 字串變得不便或無法在檔案系統、登錄、URL 和其他內容中使用。
如何建立新的發行者識別碼?
使用 PackageNameAndPublisherIdFromFamilyName 從 PackageFamilyName
擷取 PublisherId
。
使用 PackageIdFromFullName 從 PackageFullName
擷取 PublisherId
。
很少需要從 Publisher
建立 PublisherId
,但可以使用可用的 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;
}
這會藉由將套件識別碼轉換為具有結果格式 xyz_<publisherid>
的套件系列名稱來建立 PublisherId。 這個配方穩定且可靠。
這只需要從 SDK 使用 appmodel.h 進行編譯,並使用 kernel32.lib 連結 (或 kernelbase.lib、onecore.lib 或 api-ms-win-appmodel-runtime-l1.lib,如果使用 APIsets)。
了解套件識別資料中的處理器架構
常見的誤解是 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 + arm64 編譯的 notepad.exe,但 appxmanifest.xml 只能宣告 <Application Executable=...>
指向其中之一)。 假設有套件組合可讓您只安裝所需的位元,這是一件非常罕見的事情。 這不是非法的,只是進階且罕見。
此外,Architecture=x86
(或 x64|arm|arm64) 並不表示套件只包含指定架構的可執行程式碼。 這只是極為常見的案例。
注意
在此內容中討論「程式碼」或「可執行程式碼」時,我們指的是可攜式可執行 (PE) 檔案。
套件識別資料是否區分大小寫?
大部分情況下不區分大小寫,但 Publisher
區分大小寫。
其餘欄位 (Name
、ResourceId
、PublisherId
、PackageFullName
和 PackageFamilyName
) 不區分大小寫。 這些欄位會保留大小寫,但比較時不區分大小寫。