Share via


Panoramica dell'identità del pacchetto nelle app di Windows

L'identità del pacchetto è un identificatore univoco nello spazio e nel tempo. Proprio come il DNA identifica in modo univoco l'utente, l'identità del pacchetto identifica in modo univoco un pacchetto.

Un pacchetto ha un set associato di bit (file e così via). Nessun pacchetto ha la stessa identità e le modifiche apportate ai bit associati a un pacchetto richiedono un'identità diversa.

Che cos'è l'identità del pacchetto?

Un'identità del pacchetto è una costruzione logica che identifica in modo univoco un pacchetto. L'identità ha 5 parti:

  • Nome: nome scelto dallo sviluppatore dell'app. Microsoft Store applica l'univocità a tutti i nomi delle app per tutti gli sviluppatori di app nello Store, ma non è garantito che i nomi siano univoci nell'ecosistema generale.
  • Versione: numero di versione del pacchetto. Lo sviluppatore dell'app può scegliere numeri di versione arbitrari, ma deve assicurarsi che i numeri di versione aumentino con gli aggiornamenti.
  • Architettura: architettura del processore di destinazione del pacchetto. La stessa app può essere compilata per architetture del processore diverse, con ogni build che risiede nel proprio pacchetto.
  • ResourceId: stringa scelta dallo sviluppatore dell'app per identificare in modo univoco i pacchetti di risorse, ad esempio lingue diverse o scale di visualizzazione diverse. I pacchetti di risorse sono in genere indipendenti dall'architettura. Per i bundle, il ResourceId è sempre ~.
  • Editore: nome soggetto dello sviluppatore dell'app identificato dal certificato di firma. Ciò è teoricamente univoco per ogni sviluppatore di app, perché le autorità di certificazione affidabili usano nomi e identità reali univoci per popolare il campo del nome soggetto del certificato.

Questa costruzione viene talvolta definita tupla in 5 parti

Nota

I pacchetti non firmati (1) richiedono ancora un Editore, (2) l'editore deve contenere l'indicatore non firmato (OID.2.25.311729368913984317654407730594956997722=1), (3) l'indicatore non firmato deve essere l'ultimo campo nella stringa dell'editore e (4) non esiste alcun certificato o firma per un pacchetto non firmato.

Limiti dei campi di identità del pacchetto

Campo Tipo di dati Limiti Commenti
Nome Stringa pacchetto Min: 3
Max: 50
Valori consentiti per l'API Validate (vedi Stringa pacchetto)
Versione DotQuad Min: 0.0.0.0
Max: 65535.65535.65535.65535
Il formato stringa usa la notazione punteggiata base 10, "Major.Minor.Build.Revision"
Architettura Enumerazione Min: N/D
Max: N/D
I valori consentiti sono "neutral", "x86", "x64", "arm64", "x86a64"
ResourceId Stringa pacchetto Min: 0
Max: 30
Valori consentiti per l'API Validate (vedi Stringa pacchetto)
Publisher Stringa Min: 1
Max: 8192
Valori consentiti per X.509
PublisherId Stringa Min: 13
Max: 13
Codifica Base32, variante Crockford, ad esempio [a-hjkmnp-tv-z0-9]

Cos'è una stringa di pacchetto?

Una stringa di pacchetto è una stringa che consente i caratteri seguenti:

  • Caratteri di input consentiti (subset ASCII)
    • Lettere maiuscole (da U+0041 a U+005A)
    • Lettere minuscole (da U+0061 a U+007A)
    • Numeri (da U+0030 a U+0039)
    • Punto (U+002E)
    • Trattino (U+002D)

I valori seguenti non possono essere usati come stringhe di pacchetto:

Condizione Valori non consentiti
Non possono essere uguali a ".", "..", "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"
Non possono iniziare con "con.", "prn.", "aux.", "nul.", "com1.", "com2.", "com3.", "com4.", "com5.", "com6.", "com7.", "com8.", "com9.", "lpt1.", "lpt2.", "lpt3.", "lpt4.", "lpt5.", "lpt6.", "lpt7.", "lpt8.", "lpt9.", "xn--"
Non possono terminare con "."
Non possono contenere ".xn--"

È necessario confrontare una stringa di pacchetto usando API di confronto tra stringhe ordinali senza distinzione tra maiuscole e minuscole, ad esempio _wcsicmp.

I campi name e resourceid dell'identità del pacchetto sono stringhe di pacchetto.

Oggetto PackageId

Un PackageID è un oggetto contenente la tupla in 5 parti come singoli campi (Name, Version, Architecture, ResourceId, Publisher).

Nome completo pacchetto

Un nome completo del pacchetto è una stringa opaca derivata da tutte le 5 parti dell'identità di un pacchetto (nome, versione, architettura, resourceid, editore)

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

Ad esempio, un nome completo del pacchetto per l'app Windows Foto è "Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe", dove "Microsoft.Windows.Photos" è il nome, "2020.20090.1002.0" è il numero di versione, "x64" è l'architettura del processore di destinazione, l'ID risorsa è vuoto (nessun contenuto tra gli ultimi due caratteri di sottolineatura) e "8wekyb3d8bbwe" è l'ID editore per Microsoft.

Il nome completo del pacchetto identifica in modo univoco un pacchetto o un bundle MSIX. Avere due pacchetti o bundle con contenuti diversi, ma con lo stesso nome completo del pacchetto è un errore.

Nota

MSIX è il nuovo nome del termine precedente APPX. Per ulteriori informazioni, vedi Cos'è MSIX?

Nome della famiglia di pacchetti

Un nome della famiglia di pacchetti è una stringa opaca derivata da solo due parti di un'identità del pacchetto: nome e autore:

<Name>_<PublisherId>

Ad esempio, il nome della famiglia di pacchetti dell'app Windows Foto è "Microsoft.Windows.Photos_8wekyb3d8bbwe", dove "Microsoft.Windows.Photos" è il nome e "8wekyb3d8bbwe" è l'ID editore per Microsoft.

Il nome della famiglia di pacchetti viene spesso definito "nome completo del pacchetto senza versione".

Nota

Questo non è strettamente vero, poiché anche il nome della famiglia di pacchetti non dispone dell'architettura e dell'ID risorsa.

Nota

I dati e la sicurezza sono in genere limitati a una famiglia di pacchetti. Ad esempio, nel caso in cui configurassi l'app Blocco note installata da un pacchetto blocco note versione 1.0.0.0 per abilitare Wordwrap, avresti una pessima esperienza. Il Blocco note è stato successivamente aggiornato alla versione 1.0.0.1 e i dati di configurazione non sono stati spostati nella versione più recente del pacchetto.

Id editore

Il nome della famiglia di pacchetti è una stringa con il formato seguente:

<name>_<publisherid>

dove l'Id editore ha alcune proprietà molto specifiche:

  • Derivato dall'editore
  • Lunghezza minima = Lunghezza massima = 13 caratteri [dimensione fissa]
  • Caratteri consentiti (come regex) = a-hj-km-np-tv-z0-9
    • Base-32, Crockford Variant, ad esempio alfanumerico (A-Z0-9), tranne I, L (elle), O oppure U
  • Senza distinzione tra maiuscole e minuscole ordinali per i confronti --- ABCDEFABCDEFG == abcdefabcdefg

Quindi non vedrai mai % : \ / " ? o altri caratteri in un ID editore.

Per altri dettagli, vedi PackageFamilyNameFromId e PackageNameAndPublisherIdFromFamilyName.

L'ID editore viene spesso definito PublisherId.

Perché esiste l'Id editore?

L'ID editore esiste perché l'editore deve corrispondere al nome/firmatario X.509 del certificato, pertanto:

  • Può essere molto grande (lunghezza <= 8192 caratteri)
  • Può includere caratteri particolari o limitati (barra rovesciata e così via)

Questi problemi possono rendere alcune stringhe X.509 scomode o impossibili da usare nel file system, nel registro di sistema, negli URL e in altri contesti.

Come si crea un PublisherId?

Usa PackageNameAndPublisherIdFromFamilyName per estrarre PublisherId da un PackageFamilyName.

Usa PackageIdFromFullName per estrarre PublisherId da un PackageFullName.

È raro che sia necessario creare un PublisherId da Publisher, ma può essere fatto con tramite le API disponibili:

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

Di seguito è riportata un'implementazione classica di Windows C della stessa operazione:

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

In questo modo, il PublisherId viene creato convertendo un ID pacchetto in un nome di famiglia di pacchetti con il formato xyz_<publisherid> risultante. Questa ricetta è stabile e affidabile.

Questo richiede solo la compilazione con appmodel.h dall'SDK e il collegamento con kernel32.lib (o kernelbase.lib, onecore.lib o api-ms-win-appmodel-runtime-l1.lib se si usano APIIsets).

Informazioni sull'architettura del processore nell'identità del pacchetto

Spesso si pensa erroneamente che Architecture=x64 indica che il pacchetto può contenere solo codice x64. Risposta errata. Significa che il pacchetto funziona nei sistemi che supportano il codice x64 e può essere usato dalle app x64. È possibile creare un pacchetto contenente solo file PDF, ma devi dichiararlo con <Identity Architecture=x64...>, in quanto è destinato solo a essere installato nei sistemi compatibili con x64; ad esempio, i pacchetti x64 possono essere installati solo in sistemi x64 e (a partire da Windows 11) Arm64, perché i sistemi x86, Arm e Windows 10 Arm64 non supportano x64.

Un'altra incomprensione è la seguente: Architecture=neutralnon significa che il pacchetto non contiene codice eseguibile. Significa che il pacchetto funziona su tutte le architetture. Ad esempio, è possibile creare un pacchetto contenente un'API di crittografia AES scritta in JavaScript, Python, C# e così via, ma le prestazioni non sono accettabili nei sistemi Arm64. Includi quindi un file binario Arm64 ottimizzato e implementi l'API per gestirlo:

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

In alternativa, è possibile creare un pacchetto neutro con più varianti:

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

Gli sviluppatori possono quindi LoadLibrary("bin\encrypt-" + cpu + ".dll") per ottenere il file binario appropriato per il processo in fase di esecuzione.

In genere, i pacchetti neutri non hanno contenuto per architettura, ma possono averne. Esistono limiti a ciò che è possibile fare (ad esempio, è possibile creare un pacchetto del Blocco note contenente notepad.exe compilato per x86 + x64 + arm + arm64, ma appxmanifest.xml può dichiarare <Application Executable=...> solo puntando a uno di essi). Dato che ci sono bundle che consentono di installare solo i bit necessari, quest'azione non è molto comune. Non è illegale, solo avanzata ed esotica.

Inoltre, Architecture=x86 (o x64|arm|arm64) non indica che il pacchetto contiene solo codice eseguibile per l'architettura specificata. Si tratta solo di un caso molto comune.

Nota

Quando si parla di "codice" o "codice eseguibile" in questo contesto, si fa riferimento a file eseguibili portabili (PE).

L'identità del pacchetto fa distinzione tra maiuscole e minuscole?

Per lo più no, ma Publisher sì.

I campi rimanenti (Name, ResourceId, PublisherIdPackageFullName e PackageFamilyName) non fanno alcuna distinzione. Questi campi mantengono maiuscole/minuscole, ma non ne fanno distinzione.

Vedi anche

Identità pacchetto

PackageFamilyNameFromId

PackageNameAndPublisherIdFromFamilyName