Freigeben über


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

Die Paketidentität ist ein raum- und zeitübergreifend eindeutiger Bezeichner. Durch die Paketidentität wird ein Paket eindeutig identifiziert – genau wie Sie durch Ihre DNS.

Ein Paket verfügt über einen zugeordneten Satz von Bits (Dateien usw.). Es gibt niemals zwei Pakete mit der gleichen Identität, und jegliche Änderung an den Bits, die einem Paket zugeordnet sind, erfordert eine andere Identität.

Was ist eine Paketidentität?

Eine Paketidentität ist ein logisches Konstrukt, das ein Paket eindeutig identifiziert. Die Identität besteht aus fünf Teilen:

  • Name: Der vom App-Entwickler gewählte Name. 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: Die Versionsnummer des Pakets. Der App-Entwickler kann beliebige Versionsnummern verwenden, die Versionsnummern müssen sich jedoch nach jedem Update erhöhen.
  • Architektur: Die Prozessorarchitektur, für die das Paket vorgesehen ist. Eine App kann für unterschiedliche Prozessorarchitekturen erstellt werden. In diesem Fall befinden sich die einzelnen Builds jeweils in einem eigenen Paket.
  • Ressourcen-ID: Eine Zeichenfolge, die vom App-Entwickler gewählt wurde, um Ressourcenpakete eindeutig zu identifizieren – beispielsweise unterschiedliche Sprachen oder unterschiedliche Displaygrößen. Ressourcenpakete sind in der Regel architekturneutral. Bei Bündeln lautet die Ressourcen-ID immer ~.
  • Herausgeber: Der im Signaturzertifikat angegebene Antragstellername des App-Entwicklers. Diese Angabe ist theoretisch für jeden App-Entwickler eindeutig, da seriöse Zertifizierungsstellen im Feld für den Antragstellernamen des Zertifikats eindeutige reale Namen und Identitäten angeben.

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 Einschränkungen 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
Aufbau 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 String Min: 1
Max.: 8.192
Zulässige Werte gemäß X.509
PublisherId String 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 einschließlich U+0039)
    • Punkt (U+002E)
    • Bindestrich (U+002D)

Folgende Werte dürfen nicht als Paketzeichenfolgen verwendet werden:

Bedingung 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 "."
Darf Folgendes nicht enthalten „.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>

Ein vollständiger Paketname für die Fotos-App lautet beispielsweise „Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe“. Dabei ist „Microsoft.Windows.Photos“ der Name, „2020.20090.1002.0“ die Versionsnummer und „x64“ die Zielprozessorarchitektur. Die Ressourcen-ID ist leer (kein Inhalt zwischen den letzten beiden Unterstrichen), und „8wekyb3d8bbwe“ ist die Herausgeber-ID für Microsoft.

Der vollständige Paketname identifiziert ein MSIX-Paket oder -Bündel eindeutig. Es dürfen nicht zwei Pakete oder Bündel mit unterschiedlichem Inhalt, aber gleichem vollständigem Paketnamen vorhanden sein.

Hinweis

MSIX ist der neue Name des vorherigen Begriffs 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>

Der Paketfamilienname der Fotos-App lautet beispielsweise „Microsoft.Windows.Photos_8wekyb3d8bbwe“. Hierbei ist „Microsoft.Windows.Photos“ der Name und „8wekyb3d8bbwe“ die Herausgeber-ID für Microsoft.

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, und Ihre Konfigurationsdaten bei einem anschließenden Update auf die Version 1.0.0.1 nicht in die neuere Version des Pakets übernommen werden.

Herausgeber-ID

Ein Paketfamilienname ist eine Zeichenfolge im folgenden Format:

<name>_<publisherid>

Die Herausgeber-ID hat einige sehr spezifische Eigenschaften:

  • Abgeleitet vom Herausgeber
  • Mindestlänge (MinLength) und maximale Länge (MaxLength) sind jeweils 13 Zeichen (feste Größe)
  • Zulässige Zeichen (als regulärer Ausdruck): 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 gibt es die Herausgeber-ID?

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

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

Dies kann dazu führen, dass einige X.509-Zeichenfolgen nur umständlich oder gar nicht im Dateisystem, in der Registrierung, in URLs und in anderen Kontexten verwendet werden können.

Wie wird eine Herausgeber-ID erstellt?

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{ 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;
}

Hier sehen Sie eine klassische Windows-C-Implementierung des gleichen 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_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;
}

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

Sie erfordert lediglich die Kompilierung mit „appmodel.h“ aus dem SDK und die Verknüpfung mit „kernel32.lib“ (oder „kernelbase.lib“, „onecore.lib“ oder „api-ms-win-appmodel-runtime-l1.lib“ bei Verwendung von „APIsets“).

Grundlegendes zur Prozessorarchitektur in der Paketidentität

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

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

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

Sie können auch 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 zur Laufzeit die passende Binärdatei für ihren Prozess abzurufen.

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 verboten, aber etwas für fortgeschrittene Benutzer und eher ausgefallen.

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 einfach in den meisten Fällen so.

Hinweis

Mit „Code“ oder „ausführbarer Code“ sind in diesem Kontext portierbare, ausführbare Dateien (Portable Executable, PE) gemeint.

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.

Weitere Informationen

Paketidentität

PackageFamilyNameFromId-Funktion (appmodel.h)

PackageNameAndPublisherIdFromFamilyName-Funktion (appmodel.h)