Compartir a través de


Información general sobre la identidad de los paquetes en aplicaciones de Windows

La identidad del paquete es un identificador único en el espacio y el tiempo. Al igual que el ADN nos identifica de forma única, la identidad del paquete identifica de forma única un paquete.

Un paquete tiene un conjunto asociado de bits (archivos, etc.). No hay dos paquetes con la misma identidad y los cambios en los bits asociados a un paquete requieren una identidad diferente.

¿Qué es la identidad del paquete?

La identidad del paquete es una construcción lógica que identifica de forma única un paquete. Está formada por cinco partes:

  • Nombre: el nombre elegido por el desarrollador de la aplicación. Microsoft Store exige a todos los desarrolladores de aplicaciones de Store que los nombres de las aplicaciones sean únicos, pero no se garantiza que los nombres sean únicos en el ecosistema general.
  • Versión: número de versión del paquete. El desarrollador de aplicaciones puede elegir números de versión arbitrarios, pero debe asegurarse de que los números de versión aumenten con las actualizaciones.
  • Arquitectura: arquitectura del procesador al que va dirigido el paquete. La misma aplicación puede desarrollarse para diferentes arquitecturas de procesador, cada una de ellas en su propio paquete.
  • ResourceId: cadena elegida por el desarrollador de aplicaciones para identificar de forma única los paquetes de recursos, por ejemplo, diferentes idiomas o escalas de pantalla. Los paquetes de recursos suelen ser independientes de la arquitectura. En el caso de los conjuntos de productos, ResourceId es siempre ~.
  • Editor: nombre del firmante del desarrollador de aplicaciones identificado por su certificado de firma. En teoría es único para cada desarrollador de aplicaciones, ya que las entidades de certificación reputadas usan nombres e identidades reales únicas para rellenar el campo de nombre del firmante del certificado.

Esta construcción se conoce a veces como tupla de 5 partes.

Nota

Los paquetes sin firmar (1) todavía requieren un editor, (2) el editor debe contener el marcador Sin firmar (OID.2.25.31172936891398431765440773059495697722=1), (3) el marcador Sin firmar debe ser el último campo de la cadena del editor y (4) no hay ningún certificado o firma para un paquete sin firmar.

Límites de los campos de identidad del paquete

Campo Tipo de datos Límites Comentarios
Nombre Cadena de paquete Mín.: 3
Máx.: 50
Valores permitidos por Validate API (consulte Cadena de paquete)
Versión DotQuad Mín.: 0.0.0.0
Máx.: 65535.65535.65535.65535
El formato de cadena usa la notación de puntos de base 10, "Major.Minor.Build.Revision"
Architecture Enumeración Mín.: N/A
Máx.: N/A
Los valores permitidos son "neutral", "x86", "x64", "arm64", "x86a64"
ResourceId Cadena de paquete Mín.: 0
Máx.: 30
Valores permitidos por Validate API (consulte Cadena de paquete)
Publicador String Mín.: 1
Máx.: 8192
Valores permitidos por X.509
PublisherId String Mín.: 13
Máx.: 13
Codificado en Base32, variante de Crockford, es decir, [a-hjkmnp-tv-z0-9]

¿Qué es una "cadena de paquete"?

Una cadena de paquete es una cadena que permite los siguientes caracteres:

  • Caracteres de entrada permitidos (subconjunto ASCII)
    • Letras mayúsculas (U+0041 a U+005A)
    • Letras minúsculas (U+0061 a U+007A)
    • Números (U+0030 a U+0039)
    • Punto (U+002E)
    • Guion (U+002D)

Se prohíbe usar los valores siguientes como cadenas de paquete:

Condición Valores prohibidos
No puede ser igual que ".", "..", "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9"
No puede comenzar por "con.", "prn.", "aux.", "nul.", "com1.", "com2.", "com3.", "com4.", "com5.", "com6.", "com7.", "com8.", "com9.", "lpt1.", "lpt2.", "lpt3.", "lpt4.", "lpt5.", "lpt6.", "lpt7.", "lpt8.", "lpt9.", "xn--"
No puede finalizar con "."
No puede contener ".xn--"

Una cadena de paquete se debe comparar mediante las API de comparación de cadenas sin distinción entre mayúsculas y minúsculas ordinales (por ejemplo, _wcsicmp).

Los campos name y resourceid de la identidad del paquete son cadenas de paquete.

Objeto PackageId

PackageId es un objeto que contiene la tupla de 5 partes como campos individuales (Name, Version, Architecture, ResourceId, Publisher).

Nombre completo de paquete

El nombre completo de paquete es una cadena opaca derivada de las 5 partes de la identidad de un paquete (nombre, versión, arquitectura, resourceid, editor).

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

Por ejemplo, un nombre completo de paquete para la aplicación Fotos de Windows es "Microsoft.Windows.Photos_2020.20090.1002.0_x64___8wekyb3d8bbwe", donde "Microsoft.Windows.Photos" es el nombre, "2020.20090.1002.0" es el número de versión, "x64" es la arquitectura del procesador de destino, el identificador de recurso está vacío (sin contenido entre los dos últimos caracteres de subrayado) y "8wekyb3d8bbwe" es el identificador del editor de Microsoft.

El nombre completo del paquete identifica de forma única un paquete o un conjunto de productos MSIX. Es un error tener dos paquetes o conjuntos de productos con contenido diferente, pero con el mismo nombre completo del paquete.

Nota

MSIX es el nuevo nombre para el término anterior APPX. Para más información, consulte ¿Qué es MSIX?

Nombre de familia del paquete

El nombre de familia de paquete es una cadena opaca derivada de solo dos partes de una identidad del paquete: nombre y editor:

<Name>_<PublisherId>

Por ejemplo, el nombre de familia de paquete de la aplicación Fotos de Windows es "Microsoft.Windows.Photos_8wekyb3d8bbwe", donde "Microsoft.Windows.Photos" es el nombre y "8wekyb3d8bbwe" es el identificador del editor de Microsoft.

El nombre de familia de paquete se conoce a menudo como "nombre completo del paquete menos la versión".

Nota

Esto no es estrictamente cierto, ya que el nombre de familia de paquete también carece de arquitectura e identificador de recurso.

Nota

Los datos y la seguridad suelen estar limitados a una familia de paquetes. Por ejemplo, sería una mala experiencia si configurara la aplicación Bloc de notas instalada desde un paquete Bloc de notas versión 1.0.0.0 para permitir el ajuste de línea. Posteriormente, el Bloc de notas se actualizaría a la versión 1.0.0.1 y los datos de configuración no se transferirían a la nueva versión del paquete.

Id. del editor

Un nombre de familia de paquete es una cadena con el formato:

<name>_<publisherid>

donde el identificador del editor tiene algunas propiedades muy específicas:

  • Se deriva del editor
  • MinLength = MaxLength = 13 caracteres [tamaño fijo]
  • Caracteres permitidos (como expresión regular) = a-hj-km-np-tv-z0-9
    • Base-32, variante Crockford, es decir, alfanumérico (A-Z0-9) excepto sin I (isla), L (ella), O (ola) o U (usted)
  • Sin distinción entre mayúsculas y minúsculas ordinales para las comparaciones: ABCDEFABCDEFG == abcdefabcdefg

Por lo tanto, nunca verá % : \ / " ? u otros caracteres en un identificador de editor.

Consulte PackageFamilyNameFromId y PackageNameAndPublisherIdFromFamilyName para más información.

El identificador del editor se conoce a menudo como PublisherId.

¿Por qué existe el identificador del editor?

El identificador del editor existe porque el editor debe coincidir con el nombre o firmante X.509 del certificado, por lo tanto:

  • Puede ser muy grande (longitud <= 8192 caracteres)
  • Puede incluir caracteres que son difíciles o restringidos (barra diagonal inversa, etc.).

Estos problemas pueden hacer que algunas cadenas X.509 sean difíciles o imposibles de usar en el sistema de archivos, el registro, las direcciones URL y otros contextos.

¿Cómo se crea un objeto PublisherId?

Use PackageNameAndPublisherIdFromFamilyName para extraer el objeto PublisherId de PackageFamilyName.

Use PackageIdFromFullName para extraer el objeto PublisherId de PackageFullName.

No es habitual que tenga que crear un objeto PublisherId a partir de Publisher, pero se puede hacer con el uso de las 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;
}

A continuación se muestra una implementación clásica de Windows C de la misma operación:

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

Se crea el objeto PublisherId convirtiendo un identificador de paquete en un nombre de familia de paquete con el formato xyz_<publisherid> resultante. Esta receta es estable y confiable.

Para ello solo hace falta compilar con appmodel.h desde el SDK y vincularlo con kernel32.lib (o kernelbase.lib, onecore.lib o api-ms-win-appmodel-runtime-l1.lib si usa APIets).

Descripción de la arquitectura del procesador en la identidad del paquete

Un malentendido común es que Architecture=x64 significa que el paquete solo puede contener código x64. Esto no es cierto. Significa que el paquete funciona en sistemas que admiten código x64 y pueden usarlo las aplicaciones x64. Podría crear un paquete que contenga solo archivos PDF pero declararlo con <Identity Architecture=x64...> porque solo está pensado para instalarse en sistemas compatibles con x64 (por ejemplo, los paquetes x64 solo se pueden instalar en sistemas x64 y (a partir de Windows 11) Arm64, ya que los sistemas x86, Arm y Windows 10 Arm64 no admiten x64).

Incluso se entiende menos, Architecture=neutralno significa que el paquete no contenga código ejecutable. Significa que el paquete funciona en todas las arquitecturas. Por ejemplo, podría crear un paquete que contenga una API de cifrado AES escrita en JavaScript, Python, C#, etc. pero el rendimiento no es aceptable en los sistemas Arm64. Por lo tanto, incluya un binario arm64 optimizado e implemente la API para controlarlo:

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

O bien, puede crear un paquete neutral con muchas variantes:

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

Los desarrolladores pueden usar entonces LoadLibrary("bin\encrypt-" + cpu + ".dll") para obtener el binario adecuado para su proceso en tiempo de ejecución.

Normalmente, los paquetes neutrales no tienen contenido por arquitectura, pero pueden tenerlo. Hay límites para lo que uno puede hacer (por ejemplo, podría crear un paquete de Bloc de notas que contenga notepad.exe compilado para x86 + x64 + arm + arm64, pero appxmanifest.xml solo puede declarar <Application Executable=...> apuntando a uno de ellos). Dado que hay conjuntos de productos que permiten instalar solo los bits necesarios, esto es algo muy poco habitual. No es ilegal, solo avanzado y exótico.

Además, Architecture=x86 (o x64|arm|arm64) no significa que el paquete solo contenga código ejecutable para la arquitectura especificada. Es solo el caso más común.

Nota

Al hablar de "código" o "código ejecutable" en este contexto, nos referimos a archivos ejecutables portables (PE).

¿La identidad del paquete distingue mayúsculas de minúsculas?

En general, no, pero Publisher distingue mayúsculas de minúsculas.

El resto de campos (Name, ResourceId, PublisherIdPackageFullName y PackageFamilyName) no lo hace. Estos campos conservarán las mayúsculas o minúsculas, pero compararán la falta de distinción entre mayúsculas y minúsculas.

Consulte también

Identidad del paquete

PackageFamilyNameFromId

PackageNameAndPublisherIdFromFamilyName