Uma visão geral do identificador de pacote em aplicativos do Windows
O identificador de pacote é um identificador exclusivo em relação a espaço e tempo. Assim como seu DNA identifica você exclusivamente, o identificador de pacote identifica um pacote exclusivamente.
Um pacote tem um conjunto associado de bits (arquivos etc.). Dois pacotes nunca têm a mesma identidade e as alterações nos bits associados a um pacote exigem uma identidade diferente.
O que é o identificador de pacote?
Uma identificador de pacote é um constructo lógico, que identifica um pacote exclusivamente. O identificador tem cinco partes:
- Nome: esse é um nome escolhido pelo desenvolvedor do aplicativo. A Microsoft Store impõe a exclusividade de todos os nomes de aplicativos a todos os desenvolvedores de aplicativos na Store, mas não há garantia de que os nomes sejam exclusivos no ecossistema geral.
- Versão: número de versão do pacote. O desenvolvedor do aplicativo pode escolher números de versão aleatórios, mas precisa garantir que os números de versão aumentem com as atualizações.
- Arquitetura: a arquitetura do processador de destino do pacote. O mesmo aplicativo pode ser criado visando diferentes arquiteturas de processador, com cada build residindo em seu próprio pacote.
- ResourceId: uma cadeia de caracteres escolhida pelo desenvolvedor do aplicativo para identificar pacotes de recursos exclusivamente, por exemplo, idiomas diferentes ou diferentes escalas de exibição. Normalmente, os pacotes de recursos são neutros em relação à arquitetura. Para pacotes agrupados, o ResourceId é sempre
~
. - Editor: o nome da entidade do desenvolvedor do aplicativo, identificado pelo certificado de assinatura. Teoricamente, essa parte é exclusiva de cada desenvolvedor de aplicativos, pois as autoridades de certificação respeitáveis usam nomes e identidades do mundo real exclusivos para preencher o campo de nome da entidade do certificado.
Às vezes, esse constructo é chamado de tupla de cinco partes.
Observação
Pacotes não assinados (1) ainda exigem um Editor, (2) o Editor precisa conter o marcador Não assinado (OID.2.25.311729368913984317654407730594956997722=1), (3) o marcador Não assinado precisa ser o último campo na cadeia de caracteres do Editor e (4) não há certificado ou assinatura para um pacote Não assinado.
Limites de campos do identificador de pacote
Campo | Tipo de dados | Limites | Comentários |
---|---|---|---|
Nome | Cadeia de caracteres do pacote | Mínimo: 3 Máximo: 50 |
Valores permitidos por API de validação (confira Cadeia de caracteres de pacote) |
Versão | DotQuad | Mínimo: 0.0.0.0 Máximo: 65535.65535.65535.65535 |
O formulário de cadeia de caracteres usa a notação com pontos de base 10, "Major.Minor.Build.Revision" |
Arquitetura | Enumeração | Mínimo: N/A Máximo: N/A |
Os valores permitidos são "neutral", "x86", "x64", "arm", "arm64", "x86a64" |
ResourceId | Cadeia de caracteres do pacote | Mínimo: 0 Máximo: 30 |
Valores permitidos por API de validação (confira Cadeia de caracteres de pacote) |
Publisher | Cadeia de caracteres | Mín.: 1 Máximo: 8192 |
Valores permitidos por X.509 |
PublisherId | String | Mínimo: 13 Máximo: 13 |
Codificado em Base32, variante Crockford, ou seja, [a-hjkmnp-tv-z0-9] |
O que é uma 'Cadeia de caracteres de pacote'?
Uma cadeia de caracteres de pacote é aquela que permite os seguintes caracteres:
- Caracteres de entrada permitidos (subconjunto ASCII)
- Letras maiúsculas (de U+0041 até U+005A)
- Letras minúsculas (de U+0061 até U+007A)
- Números (de U+0030 até U+0039)
- Ponto (U+002E)
- Traço (U+002D)
É proibido o uso dos seguintes valores como cadeias de caracteres de pacote:
Condição | Valores proibidos |
---|---|
Não pode ser igual a | ".", "..", "con", "prn", "aux", "nul", "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9", "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9" |
Não pode começar com | "con.", "prn.", "aux.", "nul.", "com1.", "com2.", "com3.", "com4.", "com5.", "com6.", "com7.", "com8.", "com9.", "lpt1.", "lpt2.", "lpt3.", "lpt4.", "lpt5.", "lpt6.", "lpt7.", "lpt8.", "lpt9.", "xn--" |
Não pode terminar com | "." |
Não pode conter | ".xn--" |
Uma cadeia de caracteres de pacote precisa ser comparada usando uma API de comparação de cadeia de caracteres que não diferencia maiúsculas de minúsculas (por exemplo, _wcsicmp).
Os campos name
e resourceid
do identificador de pacote são cadeias de caracteres de pacote.
Objeto PackageId
Um PackageId é um objeto que contém a tupla de cinco partes como campos individuais (Name
, Version
, Architecture
, ResourceId
, Publisher
).
Nome completo do pacote
Um Nome completo do pacote é uma cadeia de caracteres opaca derivada das cinco partes de um identificador de pacote (nome, versão, arquitetura, resourceid, publisher)
<Name>_<Version>_<Architecture>_<ResourceId>_<PublisherId>
Por exemplo, um nome completo do pacote para o aplicativo Fotos do Windows é "Microsoft.Windows.Photos_2020.20090.1002.0_x64__8wekyb3d8bbwe", em que "Microsoft.Windows.Photos" é o nome, "2020.20090.1002.0" é o número de versão, "x64" é a arquitetura do processador de destino, a ID do recurso está vazia (não há conteúdo entre os dois últimos sublinhados) e "8wekyb3d8bbwe" é a ID do editor da Microsoft.
O Nome completo do pacote identifica exclusivamente um pacote ou um agrupamento MSIX. É um erro a presença de dois pacotes ou agrupamentos com conteúdo diferente, mas com o mesmo Nome completo do pacote.
Observação
MSIX é o novo nome do termo anterior APPX. Para obter mais informações, confira O que é o MSIX?
Package Family Name
Um Nome da Família de Pacotes é uma cadeia de caracteres opaca derivada de apenas duas partes de um identificador de pacote, nome e editor:
<Name>_<PublisherId>
Por exemplo, o Nome da Família de Pacotes do aplicativo Fotos do Windows é "Microsoft.Windows.Photos_8wekyb3d8bbwe", em que "Microsoft.Windows.Photos" é o nome e "8wekyb3d8bbwe" é a ID do editor da Microsoft.
O Nome da Família de Pacotes geralmente é chamado de "Nome completo do pacote sem versão".
Observação
Isso não é totalmente verdadeiro, pois o Nome da Família de Pacotes também não tem a arquitetura e a ID do recurso.
Observação
Os dados e a segurança normalmente têm como escopo uma família de pacotes. Por exemplo, seria uma experiência ruim configurar o aplicativo Bloco de Notas instalado por meio de um pacote do Bloco de Notas versão 1.0.0.0 para habilitar a quebra automática de linha. Depois, o Bloco de Notas foi atualizado para 1.0.0.1 e os dados de configuração não foram transferidos para a versão mais recente do pacote.
ID do editor
Um Nome da Família de Pacotes é uma cadeia de caracteres com o formato:
<name>_<publisherid>
em que a ID do editor tem algumas propriedades muito específicas:
- Derivado do editor
- MinLength = MaxLength = 13 caracteres [tamanho fixo]
- Caracteres permitidos (como regex) = a-hj-km-np-tv-z0-9
- Base-32, variante Crockford, ou seja, alfanumérico (A-Z0-9), exceto nenhum I (eye), L (ell), O (oh) ou U (you)
- Ordinal sem diferenciação de maiúsculas e minúsculas para comparações --- ABCDEFABCDEFG == abcdefabcdefg
Portanto, você nunca verá % : \ / " ? ou outros caracteres em uma ID do editor.
Confira PackageFamilyNameFromId e PackageNameAndPublisherIdFromFamilyName para obter mais detalhes.
A ID do editor geralmente é conhecida como PublisherId.
Por que a ID do editor existe?
A ID do editor existe porque o editor precisa corresponder ao nome/signatário X.509 do certificado, portanto:
- Ele pode ser muito grande (comprimento <= 8192 caracteres)
- Ele pode incluir caracteres estranhos ou restritos (barra invertida etc.)
Esses problemas podem fazer com que algumas cadeias de caracteres X.509 fiquem estranhas ou impossíveis de serem usadas no sistema de arquivos, no Registro, nas URLs e em outros contextos.
Como criar uma PublisherId?
Use PackageNameAndPublisherIdFromFamilyName para extrair o PublisherId
de um PackageFamilyName
.
Use PackageIdFromFullName para extrair o PublisherId
de um PackageFullName
.
É raro precisar criar um PublisherId
de Publisher
, mas isso pode ser feito usando APIs disponíveis:
#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;
}
Veja a seguir uma implementação clássica do Windows C da mesma operação:
#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;
}
Isso cria a PublisherId convertendo uma ID do pacote em um Nome da Família de Pacotes com o formato resultante xyz_<publisherid>
. Essa receita é estável e confiável.
Exige apenas que você compile com appmodel.h usando o SDK e vincule a kernel32.lib (ou kernelbase.lib, onecore.lib ou api-ms-win-appmodel-runtime-l1.lib em caso de uso de APIsets).
Noções básicas sobre a arquitetura do processador no identificador de pacote
Um mal-entendido comum é que Architecture=x64
significa que o pacote só pode conter código x64. Isso não é verdade. Isso significa que o pacote funciona em sistemas que dão suporte ao código x64 e pode ser usado por aplicativos x64. Você pode criar um pacote que contenha apenas arquivos PDF, mas declará-lo com <Identity Architecture=x64...>
porque ele deve ser instalado apenas em sistemas compatíveis com x64 (por exemplo, pacotes x64 só podem ser instalados em sistemas x64 e (do Windows 11 em diante) sistemas Arm64, pois os sistemas x86, Arm e Windows 10 Arm64 não são compatíveis com x64).
Outro mal-entendido ainda maior é que Architecture=neutral
não significa que o pacote não contém nenhum código executável. Isso significa que o pacote funciona em todas as arquiteturas. Por exemplo, você pode criar um pacote contendo uma API de criptografia AES escrita em JavaScript, Python, C# e outras, mas cujo desempenho não seja aceitável em sistemas Arm64. Nesse caso, você inclui um binário do Arm64 otimizado e implementa a API para lidar com ele:
void Encrypt(...)
{
HANDLE h{};
if (GetCpu() == arm64)
{
h = LoadLibrary(GetCurrentPackagePath() + "\bin\encrypt-arm64.dll")
p = GetProcAddress(h, "Encrypt")
return (*p)(...)
}
else
{
// ...call other implementation...
}
}
Ou você pode criar um pacote neutro com várias variantes:
\
bin\
encrypt-x86.dll
encrypt-x64.dll
encrypt-arm.dll
encrypt-arm64.dll
Depois, os desenvolvedores podem executar LoadLibrary("bin\encrypt-" + cpu + ".dll")
para obter o binário apropriado para o processo em tempo de execução.
Normalmente, os pacotes neutros não têm conteúdo por arquitetura, mas podem ter. Há limites para o que se pode fazer (por exemplo, você pode criar um pacote do Bloco de Notas contendo notepad.exe compilado para x86 + x64 + arm + arm64, mas o appxmanifest.xml só pode declarar <Application Executable=...>
apontando para um deles). Considerando que há agrupamentos que permitem instalar apenas os bits necessários, é muito incomum que isso seja feito. Não é ilegal, apenas avançado e exótico.
Além disso, Architecture=x86
(ou x64|arm|arm64) não significa que o pacote contenha apenas o código executável para a arquitetura especificada. É apenas o caso muito mais comum.
Observação
Ao discutir "código" ou "código executável" nesse contexto, estamos nos referindo a arquivos PE (executáveis portáteis).
O identificador de pacote diferencia maiúsculas de minúsculas?
Na maioria das vezes, não, mas Publisher
diferencia maiúsculas de minúsculas.
Os campos restantes (Name
, ResourceId
, PublisherId
PackageFullName
e PackageFamilyName
) não diferenciam. Esses campos preservam o uso de maiúsculas e minúsculas, mas fazem comparação sem diferenciar maiúsculas de minúsculas.
Confira também
Windows developer