Partager via


Vue d’ensemble de l’identité de package dans les applications Windows

identité de package est un identificateur unique dans l’espace et le temps. Tout comme votre ADN vous identifie de façon unique, l’identité du package identifie de façon unique un package.

Un package a un ensemble de bits associé (fichiers, etc.). Aucun des deux packages n’a la même identité, et toutes les modifications apportées aux bits associés à un package nécessitent une identité différente.

Qu’est-ce que l’identité du package ?

Une identité de package est une construction logique, identifiant de manière unique un package. L’identité comporte 5 parties :

  • Nom: Il s’agit d’un nom choisi par le développeur de l’application. Le Microsoft Store impose l’unicité de tous les noms d’applications pour tous les développeurs d’applications du Store, mais les noms ne sont pas garantis comme uniques dans l’écosystème général.
  • Version: Numéro de version du package. Le développeur d’applications peut choisir des numéros de version arbitraires, mais doit s’assurer que les numéros de version augmentent avec les mises à jour.
  • Architecture: Architecture du processeur ciblée par le package. La même application peut être générée ciblant différentes architectures de processeur, chaque build résidant dans son propre package.
  • ResourceId : Chaîne choisie par le développeur d’applications pour identifier de manière unique les packages de ressources, par exemple différentes langues ou différentes échelles d’affichage. Les packages de ressources sont généralement neutres en architecture. Pour les bundles, le ResourceId est toujours ~.
  • Publisher : le nom du sujet du développeur de l'application, tel qu'il est identifié par son certificat de signature. Cela est théoriquement unique pour chaque développeur d’applications, car les autorités de certification réputées utilisent des noms et des identités réels uniques pour remplir le champ de nom du sujet du certificat.

Cette construction est parfois appelée tuple en 5 parties.

Remarque

Packages non signés (1) nécessitent toujours un Éditeur, (2) l’ Éditeur doit contenir le marqueur non signé (OID.2.25.311729368913984317654407730594956997722=1), (3) le marqueur non signé doit être le dernier champ de la chaîne de l’ Éditeur, et (4) il n’existe aucun certificat ni signature pour un Package non signé.

Limites des champs d’identité de paquet

Terrain Type de données Limites Commentaires
Nom Chaîne de paquet Min : 3
Max : 50
Valeurs autorisées par l'API de validation (voir chaîne du package)
Version DotQuad Min : 0.0.0.0
Max : 65535.65535.65535.65535
La forme de chaîne utilise la notation en pointillé base 10, « Major.Minor.Build.Revision »
Architecture Énumération Min : N/A
Max : N/A
Les valeurs autorisées sont « neutres », « x86 », « x64 », « arm », « arm64 », « x86a64 »
ResourceId Chaîne de paquet Min : 0
Max : 30
Valeurs autorisées par l'API de validation (voir chaîne du package)
Éditeur Chaîne Min : 1
Max : 8192
Valeurs autorisées par X.509
ID de l’éditeur Chaîne Min : 13
Max : 13
Base32 encodée, variante Crockford, c.-à-d. [a-hjkmnp-tv-z0-9]

Qu’est-ce qu’une chaîne de paquet ?

Une chaîne de package est une chaîne qui autorise les caractères suivants :

  • Caractères d’entrée autorisés (sous-ensemble ASCII)
    • Lettres majuscules (U+0041 thru U+005A)
    • Lettres minuscules (U+0061 à U+007A)
    • Nombres (U+0030 thru U+0039)
    • Dot (U+002E)
    • Tiret (U+002D)

Les valeurs suivantes sont interdites d’être utilisées comme chaînes de package :

État Valeurs interdites
Ne peut pas être égal à « . », « .. », « con », « prn », « aux », « nul », « com1 », « com2 », « com3 », « com4 », « com5 », « com6 », « com7 », « com8 », « com9 », « lpt1 », « lpt2 », « lpt3 », « lpt4 », « lpt5 », « lpt6 », « lpt7 », « lpt8 », « lpt9 »
Impossible de commencer par « con. », « prn. », « aux. », « nul. », « com1. », « com2. », « com3. », « com4. », « com5. », « com6. », « com7. », « com8. », « com9. », « lpt1. », « lpt2. », « lpt3. », « lpt4. », « lpt5. », « lpt6. », « lpt7. », « lpt8. », « lpt9. », « xn-- »
Impossible de se terminer par "."
Ne peut pas contenir .xn--"

Une chaîne de package doit être comparée à l’aide d’API de comparaison de chaînes ordinales non sensibles à la casse (par exemple, _wcsicmp).

Les champs name et resourceid de l’identité du package sont des chaînes de caractères du package.

PackageId (objet)

Un PackageId est un objet contenant le tuple en 5 parties sous forme de champs individuels (Name, Version, Architecture, ResourceId, Publisher).

Nom complet du package

Un nom complet du package est une chaîne opaque dérivée de toutes les 5 parties de l’identité d’un package (nom, version, architecture, resourceid, publisher)

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

Par exemple, un nom complet de package pour l’application Photos Windows est « Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe », où « Microsoft.Windows.Photos » est le nom , « 2020.20090.1002.0 » est le numéro de version, « x64 » est l’architecture du processeur cible, l’ID de ressource est vide (aucun contenu entre les deux derniers traits de soulignement) et « 8wekyb3d8bbwe » est l’ID du serveur de publication pour Microsoft.

Le nom complet du package identifie de manière unique un package MSIX ou un bundle MSIX . Il s’agit d’une erreur d’avoir deux packages ou offres groupées avec un contenu différent, mais avec le même nom complet du package.

Remarque

MSIX est le nouveau nom de l'ancien terme APPX. Pour plus d’informations, consultez Qu’est-ce que MSIX ?

Nom de la famille de paquets

Un nom de famille du package est une chaîne opaque dérivée uniquement de deux parties d’une identité de package : nom et éditeur:

<Name>_<PublisherId>

Par exemple, le nom de la famille de packages de l’application Photos Windows est « Microsoft.Windows.Photos_8wekyb3d8bbwe », où « Microsoft.Windows.Photos » est le nom et « 8wekyb3d8bbwe » est l’ID d’éditeur pour Microsoft.

Le nom de la famille de packages est souvent appelé « nom complet de package sans version ».

Remarque

Cela n’est pas strictement vrai, car le nom de la famille de packages n’a pas non plus d’architecture et d’ID de ressource.

Remarque

Les données et la sécurité sont généralement étendues à une famille de packages. Par exemple, il serait médiocre si vous avez configuré l’application Bloc-notes installée à partir d’un package Bloc-notes version 1.0.0.0.0 pour activer Wordwrap. Le Bloc-notes a ensuite été mis à jour vers la version 1.0.0.1 et vos données de configuration n’ont pas été transmises à la version plus récente du package.

ID du serveur de publication

Un nom de famille de packages est une chaîne au format :

<name>_<publisherid>

où l’ID de serveur de publication possède des propriétés très spécifiques :

  • Dérivé de Publisher
  • MinLength = MaxLength = 13 caractères [taille fixe]
  • Caractères autorisés (en tant que regex) = a-hj-km-np-tv-z0-9
    • Base-32, Crockford Variant, c.-à-d. alphanumérique (A-Z0-9) sauf aucun I (œil), L (ell), O (oh) ou U (vous)
  • Non sensible à la casse ordinale pour les comparaisons --- ABCDEFABCDEFG == abcdefabcdefg

Alors vous ne verrez jamais % : \ / " ? ou d’autres caractères dans un ID de publication.

Pour plus d’informations, consultez PackageFamilyNameFromId et PackageNameAndPublisherIdFromFamilyName.

L’ID de l’éditeur est souvent appelé PublisherId.

Pourquoi l’ID de serveur de publication existe-t-il ?

L'ID de l'éditeur existe, car l'éditeur doit correspondre au nom/signataire X.509 de votre certificat, ce qui signifie que :

  • Il peut être très grand, ayant une longueur de <= 8192 caractères.
  • Il peut inclure des caractères qui sont peu conventionnels ou restreints (antislash, etc.)

Ces problèmes peuvent rendre certaines chaînes X.509 maladroits ou impossibles à utiliser dans le système de fichiers, le registre, les URL et d’autres contextes.

Comment créer un PublisherId ?

Utilisez PackageNameAndPublisherIdFromFamilyName pour extraire le PublisherId d’un PackageFamilyName.

Utilisez PackageIdFromFullName pour extraire le PublisherId d’un PackageFullName.

Il est rare de devoir créer un PublisherId à partir de Publisher, mais il est possible de le faire en utilisant les API disponibles :

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

Voici une implémentation Windows C classique de la même opération :

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

Cela crée l’PublisherId en convertissant un ID de package en nom de famille de package au format xyz_<publisherid>obtenu. Cette recette est stable et fiable.

Cette opération nécessite uniquement la compilation avec appmodel.h à partir du Kit de développement logiciel (SDK) et la liaison avec kernel32.lib (ou kernelbase.lib, onecore.lib ou api-ms-win-appmodel-runtime-l1.lib si vous utilisez DES APIIsets).

Comprendre l'architecture du processeur dans l'identité du package

Un malentendu courant signifie que Architecture=x64 le package ne peut contenir que du code x64. Ce n’est pas vrai. Cela signifie que le package fonctionne sur les systèmes prenant en charge le code x64 et peut être utilisé par les applications x64. Vous pouvez créer un package contenant uniquement des fichiers PDF, mais le déclarer, <Identity Architecture=x64...> car il est destiné uniquement à être installé sur des systèmes compatibles x64 (par exemple, les packages x64 ne peuvent être installés que sur les systèmes x64 et (à partir de Windows 11) Arm64, car les systèmes x86, Arm et Windows 10 Arm64 ne prennent pas en charge les systèmes x64).

Encore plus mal compris, Architecture=neutral n'pas signifie que le package ne contient aucun code exécutable. Cela signifie que le package fonctionne sur toutes les architectures. Par exemple, vous pouvez créer un package contenant une API de chiffrement AES écrite en JavaScript, Python, C#, etc. mais les performances ne sont pas acceptables sur les systèmes Arm64. Par conséquent, vous incluez un binaire Arm64 optimisé et implémentez l’API pour la gérer :

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

Vous pouvez également créer un package neutre avec plusieurs variantes :

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

Les développeurs peuvent ensuite LoadLibrary("bin\encrypt-" + cpu + ".dll") obtenir le fichier binaire approprié pour leur processus au moment de l’exécution.

En règle générale, les packages neutres n’ont pas de contenu par architecture, mais ils peuvent. Il existe des limites à ce que l’on peut faire (par exemple, vous pouvez créer un package bloc-notes contenant notepad.exe compilé pour x86 + x64 + arm + arm64, mais appxmanifest.xml ne peut déclarer <Application Executable=...> qu’à l’un d’eux). Étant donné qu’il existe des bundles qui vous permettent d’installer uniquement les bits nécessaires, il s’agit d’une chose très rare à faire. Ce n’est pas illégal, juste avancé et exotique.

En outre, Architecture=x86 (ou x64|arm|arm64) ne signifie pas que le package contient uniquement du code exécutable pour l’architecture spécifiée. C’est juste le cas très courant.

Remarque

Lorsque vous discutez de « code » ou de « code exécutable » dans ce contexte, nous faisons référence aux fichiers exécutables portables (PE).

L’identité du package respecte-t-elle la casse ?

En général, non, mais Publisher respecte la casse.

Les champs restants (Name, ResourceId, PublisherId, PackageFullName et PackageFamilyName) ne sont pas. Ces champs conservent la casse, mais comparent insensiblement à la casse.

Voir aussi

Identité de paquet

NomDeFamilleDuPackageDepuisId

PackageNameAndPublisherIdFromFamilyName