Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Para ver um exemplo funcional de ponta a ponta (aplicativo WPF + instalador Inno Setup), consulte o exemplo sparse-app.
Um executável de área de trabalho padrão — criado com dotnet build, MSBuild, CMake ou qualquer outra cadeia de ferramentas — não tem nenhuma identidade de pacote. Sem identidade, ele não pode usar muitas APIs de Windows modernas (notificações do sistema, tarefas em segundo plano, destinos de compartilhamento, tarefas de inicialização, APIs de dados do aplicativo e muito mais).
O empacotamento esparso confere identidade a um aplicativo sem mover seus binários para um MSIX. Você distribui um manifesto de identidade.msix minúsculo (apenas o arquivo de manifesto) e o registra ao lado do seu aplicativo instalado normalmente, utilizando um local externo. Seu .exe fica exatamente onde o instalador o coloca. Esta é a contraparte de produção de winapp create-debug-identity, que serve apenas para depuração durante o desenvolvimento.
Este guia aborda as três etapas da CLI que correspondem às três primeiras etapas do fluxo de trabalho oficial Conceder identidade a aplicativos não empacotados:
| Etapa | Command | Result |
|---|---|---|
| 1. Criar o manifesto de identidade | winapp init --exe <exe> --sparse |
sparse/appxmanifest.xml + sparse/Assets/ |
| 2. Compilar e assinar o pacote de identidade | winapp pack <appxmanifest.xml> --cert <pfx> |
<PackageName>.identity.msix |
| 3. Inserir identidade no aplicativo | winapp embed-identity <exe> |
<msix> elemento no manifesto de fusão do exe |
As etapas 4 a 5 dos documentos (registrar/cancelar o registro do pacote) são de responsabilidade do instalador – consulte a integração do Instalador.
Quando usar o empacotamento esparso
- Você já tem um instalador maduro (Inno Setup, WiX, NSIS, MSI) e não quer migrar para o MSIX para distribuição, mas precisa de APIs do Windows condicionadas à identidade.
- Seu aplicativo deve ser instalado em um caminho ou com um layout que o MSIX não permite.
- Você deseja uma alteração mínima e aditiva: mantenha o fluxo de instalação existente e adicione uma
.msixetapa de registro.
Se você estiver começando de novo e puder distribuir como MSIX, um aplicativo empacotado completo (winapp init + winapp pack <folder>) será mais simples.
Pré-requisitos
- Windows 10, versão 2004 (build 19041) ou posterior. Os pacotes esparsos dependem de
uap10:AllowExternalContent, que exige a versão 19041 ou superior. -
CLI do winapp — instale por meio do winget (ou atualize se já estiver instalado):
winget install Microsoft.WinApp --source winget -
Um certificado de assinatura de código confiável no computador de destino. Para testes locais, gere um certificado de desenvolvimento com
winapp cert generatee confie nele. Pacotes de produção devem ser assinados com um certificado cujo assunto corresponda ao manifestoPublisher.
Walkthrough
Os exemplos a seguir pressupõem um executável compilado em ./bin/Release/net8.0-windows/MyApp.exe.
Etapa 1 – Criar o manifesto de identidade esparso
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse
Isso deduz o nome do pacote, o publicador, a versão e a descrição do arquivo exe (por meio das informações de versão do arquivo) e solicita que você confirme ou substitua essas informações. Adicione --use-defaults (ou --no-prompt) para ignorar as solicitações na CI e --name / --publisher para substituir valores específicos:
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse --use-defaults `
--name "Contoso.MyApp" --publisher "CN=Contoso"
Ele grava o seguinte em uma pasta dedicada sparse/ no diretório atual por padrão (substitua com --output-dir):
-
appxmanifest.xml— um manifesto esparso com<uap10:AllowExternalContent>true</uap10:AllowExternalContent>(um elemento em<Properties>),ProcessorArchitecture="neutral", um aplicativowin32Appe o nome do executável preenchido emExecutable. -
Assets/— ativos visuais de espaço reservado (extraídos do ícone do executável, quando possível).
Por que uma
sparse/pasta e não ao lado do exe? O manifesto eAssets/são entradas em tempo de compilação consumidas porwinapp packewinapp embed-identity— nada os lê ao lado do exe em tempo de execução (a identidade em tempo de execução vem do elemento<msix>incorporado ao exe, juntamente com o local externo do pacote registrado, e o manifesto faz referência ao exe pelo nome, portanto sua localização é independente de onde o exe estiver). Gravá-los em uma pasta dedicada, sob controle de versão, os mantém fora de um diretório de saída da compilação (comobin/), que uma limpeza ou recompilação apagaria, e mantém a pasta livre de binários para que as próximas etapas permaneçam limpas.winapp packewinapp embed-identityprocuram emsparse/automaticamente, por isso você raramente precisa especificar o caminho.
Observação: O fluxo de inicialização simplificado ignora deliberadamente toda a instalação de SDKs/pacotes — os pacotes apenas de identidade não têm dependências de SDK.
Se um appxmanifest.xml já existir no diretório de destino, o init para em vez de sobrescrevê-lo (e seu Assets/). Execute novamente com --force para regenerar isso.
Verifique se o Publisher no manifesto gerado corresponde ao certificado que você usará para assinar. Edite appxmanifest.xml se necessário ou passe --publisher ao gerar.
Etapa 2 – Compilar e assinar o pacote de identidade
Aponte winapp pack para o manifesto esparso (um arquivo, não uma pasta):
winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx
Como o manifesto declara AllowExternalContent, winapp pack cria uma identidade exclusiva.msix contendo apenas o manifesto, sem binários nem recursos. A saída tem como padrão <PackageName>.identity.msix no diretório atual; use --output para alterá-la. A assinatura ocorre somente quando você passa --cert (ou --generate-cert).
Etapa 3 – Inserir identidade em seu aplicativo
Insira o <msix> elemento para que Windows conecte o exe em execução ao pacote de identidade:
# EXE mode — modify the built binary in place (uses mt.exe)
winapp embed-identity ./bin/Release/net8.0-windows/MyApp.exe
Ou mantenha o manifesto lado a lado como um arquivo versionado e recompile:
# XML mode — update an external SxS manifest, then rebuild your app
winapp embed-identity ./app.manifest
No modo XML, o <msix> elemento é inserido (ou substituído) no manifesto de destino. Referencie esse manifesto do projeto (para .NET, defina<ApplicationManifest>app.manifest</ApplicationManifest>) e recompile para que o elemento seja inserido no exe.
Ambos os modos leem a identidade de um appxmanifest.xml esparso. Quando você omite --manifest, o winapp procura primeiro em uma pasta sparse/ (onde winapp init --exe --sparse a cria por padrão) ao lado do destino primeiro e, em seguida, recorre novamente ao lado do alvo e ao diretório atual; passe --manifest para apontar para outro lugar.
Observação: O modo EXE reescreve o binário com
mt.exe, o que invalida qualquer assinatura do Authenticode existente. Assine novamente o exe (por exemplowinapp sign ./MyApp.exe <cert.pfx>) antes de distribuí-lo.
Etapa 4 – Registrar (para teste local)
Os logotipos do manifesto são resolvidos a partir do local externo em tempo de execução, e não a partir daquele .msix baseado apenas na identidade. A Etapa 1 os listou em ./sparse/Assets; portanto, copie-os para junto do seu executável (o local externo) antes de registrá-los, caso contrário, o Windows registrará um layout sem os logotipos referenciados no manifesto:
# Copy the generated assets into the external location (beside your exe)
Copy-Item ./sparse/Assets -Destination .\bin\Release\net8.0-windows\Assets -Recurse -Force
Em seguida, registre o pacote de identidade nessa pasta (o local externo):
Add-AppxPackage -Path .\MyApp.identity.msix `
-ExternalLocation (Resolve-Path .\bin\Release\net8.0-windows)
Inicie o aplicativo e confirme se a identidade está presente, por exemplo, Windows.ApplicationModel.Package.Current.Id.FamilyName deve retornar o nome da família do seu pacote em vez de gerar um erro.
Para limpar:
Remove-AppxPackage <full-package-name>
Manipulação de ativos
O .msix esparso é apenas de identidade. Os recursos visuais referenciados pelo manifesto (Assets\StoreLogo.png, blocos dinâmicos etc.) são resolvidos a partir do local de conteúdo externo em tempo de execução, isto é, do diretório de instalação do seu aplicativo, e não de dentro do .msix.
Isso significa que você deve implantar a Assets/ pasta junto com seu aplicativo (mesmo layout que o manifesto espera, em relação ao local externo).
A Etapa 2 empacota o arquivo de manifesto diretamente (winapp pack ./sparse/appxmanifest.xml), o que gera a identidade .msix a partir apenas desse manifesto: arquivos adjacentes são ignorados, portanto, ela nunca inclui seus ativos ou binários. (Se, em vez disso, você apontar winapp pack para uma pasta cujo manifesto declara AllowExternalContent, ele avisará sobre quaisquer recursos ou binários que encontrar, pois, no caso de um pacote esparso, eles pertencem ao local externo, e não dentro de .msix.)
Integração do instalador
Registro e cancelamento de registro são o trabalho do instalador. O padrão é o mesmo entre as ferramentas do instalador:
-
Instalar: copie os binários do aplicativo, a
Assets/pasta e o.msixdiretório de instalação e executeAdd-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>". -
Desinstalar: execute
Remove-AppxPackage <full-package-name>antes de excluir arquivos.
Segurança: o diretório de instalação é resolvido no momento da instalação e pode conter caracteres (por exemplo, uma aspa simples) que interrompem um literal de string do PowerShell. Sempre faça o escape ou valide o caminho antes de interpolá-lo em uma string
-Command— os trechos de código do WiX e do NSIS abaixo pressupõem um caminho de instalação confiável, enquanto o exemplo do Inno Setup demonstra como fazer o escape de forma segura. Prefira passar caminhos como argumentos para um script-Fileem vez de interpolação-Commandembutida.
Configuração do Inno
Construa os argumentos do PowerShell em uma função [Code], de modo que o caminho de instalação do runtime seja escapado para o literal do PowerShell entre aspas simples (um diretório de instalação que contenha um ' não deve permitir a injeção de script):
[Files]
Source: "dist\*"; DestDir: "{app}"; Flags: recursesubdirs
Source: "MyApp.identity.msix"; DestDir: "{app}"
[Run]
Filename: "powershell.exe"; Parameters: "{code:RegisterParams}"; Flags: runhidden
[UninstallRun]
Filename: "powershell.exe"; \
Parameters: "-NoProfile -ExecutionPolicy Bypass -Command ""Get-AppxPackage -Name 'MyApp' | Remove-AppxPackage"""; \
Flags: runhidden
[Code]
function EscapePSLiteral(const Value: string): string;
var S: string;
begin
S := Value; StringChange(S, '''', ''''''); Result := S;
end;
function RegisterParams(Param: string): string;
var AppDir: string;
begin
AppDir := ExpandConstant('{app}');
{ -ErrorAction Stop + try/catch make a registration failure terminating, so powershell.exe
exits nonzero and the AfterInstall callback (see the full sample) can abort with rollback. }
Result := '-NoProfile -ExecutionPolicy Bypass -Command "try { Add-AppxPackage -Path ''' +
EscapePSLiteral(AppDir + '\MyApp.identity.msix') +
''' -ExternalLocation ''' + EscapePSLiteral(AppDir) + ''' -ErrorAction Stop } catch { Write-Error $_; exit 1 }"';
end;
Consulte o exemplo sparse-app para ver um setup.iss completo e funcional.
Os exemplos de WiX e NSIS abaixo invocam um pequeno register-sparse.ps1 via -File para que o caminho de instalação seja passado como um parâmetro (o PowerShell o associa como dados) em vez de ser interpolado em uma -Command cadeia de caracteres. Isso evita a injeção de script por meio de um diretório de instalação criado (por exemplo, um nome de pasta que contém uma aspa ou $(...)):
# register-sparse.ps1 — ship this alongside your installer
param(
[Parameter(Mandatory)] [string] $MsixPath,
[Parameter(Mandatory)] [string] $ExternalLocation,
[Parameter(Mandatory)] [string] $PackageName
)
$ErrorActionPreference = 'Stop'
try {
# Add-AppxPackage emits NON-terminating errors by default, so a failure would otherwise leave
# the process exit code at 0 and let the installer complete without identity. Try the add
# directly first: a fresh install or a version-bumped upgrade registers/updates in place
# without touching any existing registration. -ErrorAction Stop + the outer trap make a real
# failure terminating so the installer (WiX Return="check" / NSIS) sees it.
try {
Add-AppxPackage -Path $MsixPath -ExternalLocation $ExternalLocation -ErrorAction Stop
} catch {
# Only ONE failure is safe to resolve by unregister+retry: the exact same version is already
# registered (HRESULT 0x80073CFB, ERROR_PACKAGE_ALREADY_EXISTS — "already installed,
# reinstallation blocked"), which Add-AppxPackage rejects. Re-throw everything else
# (untrusted/corrupt .msix, unsupported OS, ...) so a bad new package can NEVER unregister a
# working prior registration and strip the installed app of the identity it already had.
if ($_.Exception.HResult -ne 0x80073CFB) { throw }
Get-AppxPackage -Name $PackageName | Remove-AppxPackage -ErrorAction SilentlyContinue
Add-AppxPackage -Path $MsixPath -ExternalLocation $ExternalLocation -ErrorAction Stop
}
} catch {
Write-Error $_
exit 1
}
WiX (v3)
Registre-se por usuário (Impersonate="yes"), pois Add-AppxPackage registra o pacote para a conta que o executa. Uma ação adiada com Impersonate="no" é executada como LocalSystem, o que não concede uma identidade ao usuário que realiza a instalação (e geralmente é rejeitada). Para um MSI por máquina, execute o registro com representação para que ele se aplique ao usuário que o invoca.
Uma ação personalizada adiada não pode ler INSTALLFOLDER diretamente (ações adiadas são executadas em um contexto sem acesso a propriedades), e simplesmente declarar a ação não a executa. Portanto, encaminhe os caminhos por meio de CustomActionData — uma ação imediata do tipo 51 cujo nome Property é igual ao da ação diferida Id — e agende ambos para depois de InstallFiles:
<!-- Immediate: stash the command line (with the resolved paths) into the deferred action's
CustomActionData. Windows Installer copies the value of the property named the same as a
deferred action into that action's CustomActionData. -->
<CustomAction Id="SetRegisterSparseCmd" Property="RegisterSparse" Execute="immediate"
Value="powershell.exe -NoProfile -ExecutionPolicy Bypass -File "[INSTALLFOLDER]register-sparse.ps1" -MsixPath "[INSTALLFOLDER]MyApp.identity.msix" -ExternalLocation "[INSTALLFOLDER]" -PackageName "MyPackageIdentityName"" />
<!-- Deferred + impersonated: CAQuietExec reads its command line from CustomActionData when run
deferred, so it registers the package for the invoking user. Return="check" fails the
install if registration fails. -->
<CustomAction Id="RegisterSparse" BinaryKey="WixCA" DllEntry="CAQuietExec"
Execute="deferred" Impersonate="yes" Return="check" />
<InstallExecuteSequence>
<Custom Action="SetRegisterSparseCmd" After="InstallFiles">NOT Installed</Custom>
<Custom Action="RegisterSparse" After="SetRegisterSparseCmd">NOT Installed</Custom>
</InstallExecuteSequence>
CAQuietExec é fornecido na extensão util do WiX (WixUtilExtension); faça referência a ele para que o binário WixCA esteja disponível.
Uma única ação executada em nome de outro usuário registra a identidade apenas para o usuário que executa o instalador. Para provisionar cada usuário de uma instalação por máquina, faça o registro na primeira inicialização (por usuário) ou use um mecanismo de provisionamento, como
Add-AppxProvisionedPackage.
NSIS
Section
# Capture the PowerShell exit code and abort if registration failed. register-sparse.ps1 exits
# nonzero on failure (it sets $ErrorActionPreference='Stop' and traps), so without this check the
# installer would complete even though the app has no identity.
ExecWait 'powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$INSTDIR\register-sparse.ps1" -MsixPath "$INSTDIR\MyApp.identity.msix" -ExternalLocation "$INSTDIR" -PackageName "MyPackageIdentityName"' $0
IntCmp $0 0 +2
Abort "Registering the sparse identity package failed (exit code $0). The app requires package identity."
SectionEnd
Resolução de problemas
Package.Current gera um erro de "no package identity" em tempo de execução
- O pacote de identidade não está registrado ou o manifesto de fusão do exe está sem o
<msix>elemento. Execute novamentewinapp embed-identity(e recompile se estiver usando o modo XML) e registre-se novamente comAdd-AppxPackage -ExternalLocation. - O
<msix packageName>/ /publisherapplicationIdexe deve corresponder exatamente à identidade do pacote registrado.
Ativos/logotipos não aparecem
- Certifique-se de que a pasta
Assets/esteja implantada no local externo com os mesmos caminhos relativos que o manifesto espera. Os recursos são resolvidos a partir do local externo, não do.msix.
Add-AppxPackage falha com um erro de assinatura ou de confiança
- O
.msixdeve ser assinado com um certificado que seja confiável na máquina e cujo assunto corresponda ao manifestoPublisher. Para testes locais, gere e confie em um certificadowinapp cert generatede desenvolvimento e verifique se o manifestoPublishercorresponde a ele.
MakeAppx: "O aplicativo com o valor de RuntimeBehavior 'win32App' não deve declarar EntryPoint"
- Um aplicativo esparso
win32Appnão deve declararEntryPoint. Os manifestos gerados porwinapp init --sparsejá estão corretos; remova qualquer atributoEntryPointse você editou o manifesto manualmente.
"A entrada é um arquivo, mas não um manifesto esparso"
-
winapp pack <file>aceita apenas um manifesto que declara<uap10:AllowExternalContent>true</uap10:AllowExternalContent>. Gere um comwinapp init --exe <exe> --sparseou forneça uma pasta de entrada para criar um MSIX completo.
Consulte também
Windows developer