Eine Übersicht über die Paketidentität in Windows-Apps

Die Paketidentität ist ein raum- und zeitübergreifend eindeutiger Bezeichner. Genau wie Ihre DNA Sie eindeutig identifiziert, identifiziert die Paketidentität ein Paket eindeutig.

Ein Paket verfügt über einen zugeordneten Satz von Bits (Dateien usw.). Keine zwei Pakete weisen dieselbe Identität auf, und alle Änderungen an den Bits, die einem Paket zugeordnet sind, erfordern eine andere Identität.

Was ist eine Paketidentität?

Eine Paketidentität ist ein logisches Konstrukt, das ein Paket eindeutig identifiziert. Die Identität weist fünf Teile auf:

  • Name: Dies ist ein Name, der vom App-Entwickler ausgewählt wird. Der Microsoft Store erzwingt zwar die Eindeutigkeit aller App-Namen für alle App-Entwickler im Store, aber es ist nicht garantiert, dass Namen im allgemeinen Ökosystem eindeutig sind.
  • Version: Versionsnummer des Pakets. Der App-Entwickler kann beliebige Versionsnummern auswählen, muss jedoch sicherstellen, dass die Versionsnummern mit Updates steigen.
  • Architektur: Die Prozessorarchitektur, auf die das Paket ausgerichtet wird. Dieselbe App kann für unterschiedliche Prozessorarchitekturen erstellt werden, wobei jedes Build in seinem eigenen Paket gespeichert wird.
  • ResourceId: Eine Zeichenfolge, die vom App-Entwickler ausgewählt wurde, um Ressourcenpakete eindeutig zu identifizieren, z. B. verschiedene Sprachen oder unterschiedliche Anzeigeskalen. Ressourcenpakete sind in der Regel architekturneutral. Bei Bündeln lautet die Ressourcen-ID immer ~.
  • Herausgeber: Der im Signaturzertifikat angegebene Antragstellername des App-Entwicklers. Dies ist theoretisch für jeden App-Entwickler einzigartig, da seriöse Zertifizierungsstellen eindeutige reale Namen und Identitäten verwenden, um das Feld für den Namen des Antragstellers im Zertifikat zu füllen.

Dieses Konstrukt wird manchmal als fünfteiliges Tupel bezeichnet.

Hinweis

Nicht signierte Pakete (1) benötigen trotzdem einen Herausgeber. (2) Der Herausgeber muss die Kennzeichnung „Nicht signiert“ (OID.2.25.311729368913984317654407730594956997722=1) enthalten. (3) Die Markierung für „Nicht signiert“ muss das letzte Feld in der Zeichenfolge für den Herausgeber sein. Und: (4) Für nicht signierte Pakete gibt es kein Zertifikat und keine Signatur.

Grenzwerte für Paketidentitätsfelder

Feld Datentyp Grenzen Kommentare
Name Paketzeichenfolge Min: 3
Max.: 50
Zulässige Werte gemäß Validierungs-API (siehe Was ist eine Paketzeichenfolge?)
Version DotQuad Min.: 0.0.0.0
Max: 65535.65535.65535.65535
Zeichenfolgenformat mit durch Punkte getrennter Notation auf Zehnerbasis: Hauptversion.Nebenversion.Build.Revision
Architektur Enumeration Min.: k. A.
Max.: k. A.
Zulässige Werte sind „neutral“, „x86“, „x64“, „arm“, „arm64“, „x86a64“
ResourceId Paketzeichenfolge Min.: 0
Max.: 30
Zulässige Werte gemäß Validierungs-API (siehe Was ist eine Paketzeichenfolge?)
Herausgeber Schnur Min.: 1
Max: 8192
Zulässige Werte pro X.509
VerlegerId Schnur Min.: 13
Max.: 13
Base32-codierte Crockford-Variante, also [a-hjkmnp-tv-z0-9]

Was ist eine Paketzeichenfolge?

Eine Paketzeichenfolge ist eine Zeichenfolge, in der folgende Zeichen zulässig sind:

  • Zulässige Eingabezeichen (ASCII-Teilmenge)
    • Großbuchstaben (U+0041 bis einschließlich U+005A)
    • Kleinbuchstaben (U+0061 bis einschließlich U+007A)
    • Zahlen (U+0030 bis U+0039)
    • Punkt (U+002E)
    • Bindestrich (U+002D)

Die folgenden Werte dürfen nicht als Paketzeichenfolgen verwendet werden:

Zustand Unzulässige Werte
Darf nicht Folgendem entsprechen „.“, „..“, „con“, „prn“, „aux“, „nul“, „com1“, „com2“, „com3“, „com4“, „com5“, „com6“, „com7“, „com8“, „com9“, „lpt1“, „lpt2“, „lpt3“, „lpt4“, „lpt5“, „lpt6“, „lpt7“, „lpt8“, „lpt9“
Darf nicht mit Folgendem beginnen „con.“, „prn.“, „aux.“, „nul.“, „com1.“, „com2.“, „com3.“, „com4.“, „com5.“, „com6.“, „com7.“, „com8.“, „com9.“, „lpt1.“, „lpt2.“, „lpt3.“, „lpt4.“, „lpt5.“, „lpt6.“, „lpt7.“, „lpt8.“, „lpt9.“, „xn--“
Darf nicht mit Folgendem enden "."
Kann nicht enthalten sein „.xn--“

Eine Paketzeichenfolge muss mithilfe einer API zum Vergleichen ordnungsbasierter Zeichenfolgen ohne Berücksichtigung der Groß- und Kleinschreibung verglichen werden (also beispielsweise mithilfe von „_wcsicmp“).

Die Felder name und resourceid der Paketidentität sind Paketzeichenfolgen.

PackageId-Objekt

Eine Paket-ID (PackageId) ist ein Objekt, das das fünfteilige Tupel in Form von einzelnen Feldern (Name, Version, Architecture, ResourceId, Publisher) enthält.

Vollständiger Paketname

Ein vollständiger Paketname ist eine nicht transparente Zeichenfolge, die von allen fünf Teilen der Identität eines Pakets (Name, Version, Architektur, Ressourcen-ID und Herausgeber) abgeleitet wird.

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

Beispielsweise lautet ein vollständiger Paketname für die Windows Fotos-App "Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe", wobei "Microsoft.Windows.Photos" der Name, "2020.20090.1002.0" die Versionsnummer, "x64" die Zielprozessorarchitektur ist, die Ressourcen-ID ist leer (kein Inhalt zwischen den letzten beiden Unterstrichen), und "8wekyb3d8bbwe" ist die Publisher-ID für Microsoft.

Der vollständige Paketname identifiziert ein MSIX-Paket oder -Bündel eindeutig. Es ist ein Fehler, zwei Pakete oder Bündel mit unterschiedlichen Inhalten, aber mit demselben vollständigen Paketnamen zu haben.

Hinweis

MSIX ist der neue Name für den vorherigen Begriff APPX. Weitere Informationen finden Sie unter Was ist MSIX?

Paketfamilienname

Ein Paketfamilienname ist eine nicht transparente Zeichenfolge, die aus zwei Teilen einer Paketidentität abgeleitet wird – Name und Herausgeber:

<Name>_<PublisherId>

Beispielsweise lautet der Paketfamilienname der Windows Fotos-App "Microsoft.Windows.Photos_8wekyb3d8bbwe", wobei "Microsoft.Windows.Photos" der Name und "8wekyb3d8bbwe" die Herausgeber-ID für Microsoft ist.

Der Paketfamilienname wird häufig als „vollständiger Paketname ohne Version“ bezeichnet.

Hinweis

Dies ist streng genommen nicht ganz korrekt, da der Paketfamilienname auch keine Architektur und keine Ressourcen-ID enthält.

Hinweis

Daten und Sicherheit sind in der Regel auf eine Paketfamilie ausgerichtet. Beispielsweise wäre es ärgerlich, wenn Sie für die Editor-App, die auf der Grundlage eines Pakets mit der Editor-Version 1.0.0.0 installiert wurde, den Zeilenumbruch aktiviert haben, Dann wurde Notepad auf 1.0.0.1 aktualisiert und Ihre Konfigurationsdaten wurden nicht auf die neuere Version des Pakets übertragen.

Herausgeber-ID

Ein Paketfamilienname ist eine Zeichenfolge mit dem Format:

<name>_<publisherid>

Die Publisher-ID hat einige sehr spezifische Eigenschaften:

  • Abgeleitet vom Herausgeber
  • MinLength = MaxLength = 13 Zeichen [feste Größe]
  • Zulässige Zeichen (als regex) = a-hj-km-np-tv-z0-9
    • Base32-codierte Crockford-Variante – alphanumerisch (A–Z, 0–9) ohne „I“, „L“, „O“ und „U“
  • Ordnungsbasiert ohne Berücksichtigung der Groß-/Kleinschreibung für Vergleiche (ABCDEFABCDEFG == abcdefabcdefg)

Die Zeichen „%“, „:“, „\“, „/“, „"“, „?“ oder andere Zeichen sind niemals in einer Herausgeber-ID enthalten.

Ausführlichere Informationen finden Sie unter PackageFamilyNameFromId-Funktion (appmodel.h) sowie unter PackageNameAndPublisherIdFromFamilyName-Funktion (appmodel.h).

Die Herausgeber-ID wird häufig als „PublisherId“ bezeichnet.

Warum ist Publisher-ID vorhanden?

Die Herausgeber-ID ist vorhanden, da Herausgeber den X.509-Namen bzw. den Unterzeichner Ihres Zertifikats abgleichen müssen.

  • Es kann sehr groß sein (Länge <= 8192 Zeichen)
  • Sie kann merkwürdige oder eingeschränkte Zeichen (etwa einen umgekehrten Schrägstrich) enthalten.

Diese Probleme können es schwierig oder unmöglich machen, einige X.509-Zeichenfolgen im Dateisystem, in der Registrierung, in URLs und anderen Kontexten zu verwenden.

Wie erstelle ich eine PublisherId?

Verwenden Sie PackageNameAndPublisherIdFromFamilyName, um die Herausgeber-ID (PublisherId) aus einem Paketfamiliennamen (PackageFamilyName) zu extrahieren.

Verwenden Sie PackageIdFromFullName, um die Herausgeber-ID (PublisherId) aus einem vollständigen Paketnamen (PackageFullName) zu extrahieren.

Es ist zwar nur selten erforderlich, eine Herausgeber-ID (PublisherId) auf der Grundlage des Herausgebers (Publisher) zu erstellen, hierfür stehen aber entsprechende APIs zur Verfügung:

#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{ nameLength + 1 }; // xyz_...publisherid...
    PACKAGE_ID id{};
    id.name = name;
    id.publisher = publisher;
 
    WCHAR familyName[PACKAGE_FAMILY_NAME_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;
}

Es folgt eine klassische Windows C-Implementierung desselben Vorgangs:

#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_name) - 1 };

    PACKAGE_ID id{};
    id.name = c_name;
    id.publisher = publisher;
    WCHAR familyName[PACKAGE_FAMILY_NAME_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;
}

Hier wird zur Erstellung der Herausgeber-ID eine Paket-ID in einen Paketfamiliennamen mit dem resultierenden Format xyz_<publisherid> konvertiert. Dieses Rezept ist stabil und zuverlässig.

Dazu müssen Sie nur mit "appmodel.h" aus dem SDK kompilieren und eine Verknüpfung mit kernel32.lib (oder kernelbase.lib, onecore.lib oder api-ms-win-appmodel-runtime-l1.lib bei Verwendung von APIsets) herstellen.

Grundlegendes zur Prozessorarchitektur in der Paketidentität

Ein häufiges Missverständnis bedeutet, dass Architecture=x64 das Paket nur x64-Code enthalten kann. Das stimmt nicht. Dies bedeutet, dass das Paket auf Systemen funktioniert, die x64-Code unterstützen und von x64-Apps verwendet werden können. Sie können ein Paket erstellen, das nur PDF-Dateien enthält, aber deklarieren <Identity Architecture=x64...> , da es nur für die Installation auf x64-kompatiblen Systemen vorgesehen ist (z. B. x64-Pakete können nur auf x64- und (ab Windows 11) Arm64-Systemen installiert werden, da x86, Arm und Windows 10 Arm64-Systeme x64 nicht unterstützen).

Ein noch größeres Missverständnis: Architecture=neutral bedeutet nicht, dass das Paket keinen ausführbaren Code enthält. Dies bedeutet, dass das Paket für alle Architekturen funktioniert. Sie können beispielsweise ein Paket erstellen, das eine AES-Verschlüsselungs-API enthält, die in JavaScript, Python, C# usw. geschrieben wurde, aber die Leistung ist auf Arm64-Systemen nicht akzeptabel. Daher fügen Sie eine optimierte Arm64-Binärdatei hinzu und implementieren die API, um sie zu verarbeiten:

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

Oder Sie können ein neutrales Paket mit mehreren Varianten erstellen:

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

Entwickler können dann LoadLibrary("bin\encrypt-" + cpu + ".dll") verwenden, um die entsprechende Binärdatei für ihren Prozess während der Laufzeit zu erhalten.

In der Regel enthalten neutrale Pakete keine architekturspezifischen Inhalte, dies ist aber möglich. Nicht alles ist möglich. So können Sie zwar beispielsweise ein Editor-Paket erstellen, das „notepad.exe“ für x86, x64, arm und arm64 kompiliert enthält, aber appxmanifest.xml kann nur <Application Executable=...> mit einem Verweis auf eine der Optionen deklarieren. Da es Bundles gibt, mit denen Sie nur die erforderlichen Bits installieren können, ist das eine sehr seltene Vorgehensweise. Es ist nicht illegal, bloß fortgeschritten und exotisch.

Außerdem bedeutet Architecture=x86 (oder x64|arm|arm64) nicht, dass das Paket nur ausführbaren Code für die angegebene Architektur enthält. Es ist nur der überwältigend häufige Fall.

Hinweis

Bei der Diskussion von "Code" oder "ausführbarem Code" in diesem Kontext verweisen wir auf portable ausführbare Dateien (PE).

Wird bei der Paketidentität die Groß-/Kleinschreibung beachtet?

Größtenteils nicht. Beim Herausgeber (Publisher) allerdings schon.

Bei den restlichen Feldern (Name, ResourceId, PublisherId, PackageFullName und PackageFamilyName) wird die Groß-/Kleinschreibung nicht beachtet. Die Groß-/Kleinschreibung bleibt zwar erhalten, wird aber bei Vergleichen ignoriert.

Paketidentität im Vergleich zur Anwendungsidentität

Paketidentität und Anwendungsidentität sind miteinander verknüpft, aber unterschiedliche Konzepte. Das Verständnis des Unterschieds trägt dazu bei, eine häufige Fehleinschätzung zu vermeiden: dass eine Anwendung ihr Paket ist .

  • Ein Paket ist eine Einheit der Verteilung und Bereitstellung. Sie installieren, aktualisieren oder entfernen ein Paket als Ganzes.
  • Eine Anwendung ist ein benutzerorientiertes Konstrukt – eine Sammlung von Fenstern, Prozessen und anderen Ressourcen, die zusammen ein einzelnes Programm bilden, unabhängig davon, wie sie implementiert wird (einzelner Prozess, mehrere Prozesse oder sogar mehrere Anwendungen, die einen Prozess gemeinsam nutzen).

Anwendungen werden über Pakete verteilt, sind aber nicht dasselbe wie Pakete. Ein einzelnes Paket kann von Null bis zu hundert Anwendungen deklarieren, und einige Pakettypen (z. B. Frameworkpakete und Ressourcenpakete) können keine Anwendungen überhaupt deklarieren.

ApplicationUserModelID (AUMID)

Bei verpackten Anwendungen wird die Anwendungsidentität durch die ApplicationUserModelID (auch als AppUserModelId oder AUMID bezeichnet) dargestellt. Mit dieser Zeichenfolge können Windows windows, Prozesse und Ressourcen unabhängig von der internen Architektur der Anwendung einer bestimmten Anwendung zuordnen.

Anders ausgedrückt:

  • Die Paketidentität (das oben beschriebene 5-Teil-Tupel) identifiziert ein Paket eindeutig.
  • Die Anwendungsidentität (Application Identity , AUMID) identifiziert eine Anwendung innerhalb dieses Pakets eindeutig.

Windows verwendet die AUMID, um über Anwendungen zur Laufzeit zu gründen, z. B. um windows in der Taskleiste zu gruppieren, Benachrichtigungen weiterzuleiten und den App-Lebenszyklus zu verwalten. Die AUMID wird im Application-Element des Paketmanifests deklariert und wird vom Paketfamiliennamen und der deklarierten ID der Anwendung abgeleitet.

Tip

Eine tiefere Erkundung der Gründe, warum Anwendungen keine Pakete sind und wie AUMID die beiden Konzepte überbrückt, finden Sie unter Anwendungen sind keine Pakete im Inside MSIX-Blog.

Siehe auch