Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Per un esempio end-to-end funzionante (macchine virtuali Windows app + Programma di installazione di Inno), vedi l'esempio sparse-app.
Un eseguibile desktop standard, compilato con dotnet build, MSBuild, CMake o qualsiasi altra toolchain, non ha un'identità del pacchetto. Senza identità, non può usare molte API moderne di Windows (notifiche di tipo avviso popup, attività in background, condividere destinazioni, attività di avvio, API dati dell'app e altro ancora).
Il pacchetto sparse assegna un'identità a un'app senza spostarne i binari in un pacchetto MSIX. Distribuisci un piccolo pacchetto di sola identità.msix (solo un manifesto) e registralo insieme all'app installata normalmente usando un percorso esterno. Il tuo .exe rimane esattamente dove lo colloca il tuo installer. Questa è la controparte di produzione di winapp create-debug-identity, che è solo per il debug in fase di sviluppo.
Questa guida illustra i tre passaggi della CLI che corrispondono ai primi tre passaggi del flusso di lavoro ufficiale Grant identity to non-packaged apps:
| Passo | Comando | Result |
|---|---|---|
| 1. Creare il manifesto dell'identità | winapp init --exe <exe> --sparse |
sparse/appxmanifest.xml + sparse/Assets/ |
| 2. Compilare e firmare il pacchetto di identità | winapp pack <appxmanifest.xml> --cert <pfx> |
<PackageName>.identity.msix |
| 3. Incorporare l'identità nell'app | winapp embed-identity <exe> |
<msix> elemento nel manifesto fusion del file EXE |
I passaggi da 4 a 5 della documentazione (registrazione/annullamento della registrazione del pacchetto) sono responsabilità del programma di installazione . Vedere Integrazione del programma di installazione.
Quando usare i pacchetti sparse
- Si dispone già di un programma di installazione maturo (Installazione di Inno, WiX, NSIS, MSI) e non si vuole passare a MSIX per la distribuzione, ma sono necessarie API di Windows gestite dall'identità.
- L'app deve essere installata in un percorso o con un layout non consentito da MSIX.
- Si vuole una modifica minima e aggiuntiva: mantenere il flusso di installazione esistente e aggiungere un
.msixpassaggio di registrazione.
Se stai iniziando da zero e puoi distribuire come MSIX, un'app completa in pacchetto (winapp init + winapp pack <folder>) è più semplice.
Prerequisiti
- Windows 10 versione 2004 (build 19041) o successiva. I pacchetti di tipo sparse si basano su
uap10:AllowExternalContent, che richiede 19041+. -
winapp CLI — installa tramite winget (o aggiorna se è già installato):
winget install Microsoft.WinApp --source winget -
Certificato di firma del codice attendibile nel computer di destinazione. Per i test locali, generare un certificato di sviluppo con
winapp cert generatee considerarlo attendibile. I pacchetti di produzione devono essere firmati con un certificato il cui oggetto corrisponde al manifestoPublisher.
Walkthrough
Gli esempi seguenti presuppongono un eseguibile compilato in ./bin/Release/net8.0-windows/MyApp.exe.
Passaggio 1: Creare il manifesto dell'identità di tipo sparse
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse
Ciò deduce il nome del pacchetto, l'editore, la versione e la descrizione dall'exe (tramite le informazioni sulla versione del file) e richiede di accettarli o eseguirne l'override. Aggiungere --use-defaults (o --no-prompt) per ignorare le richieste in CI e --name / --publisher per eseguire l'override di valori specifici:
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse --use-defaults `
--name "Contoso.MyApp" --publisher "CN=Contoso"
Per impostazione predefinita, scrive quanto segue nella cartella dedicata sparse/ nella directory corrente (sovrascrivibile con --output-dir):
-
appxmanifest.xml— un manifesto essenziale con<uap10:AllowExternalContent>true</uap10:AllowExternalContent>(un elemento sotto<Properties>),ProcessorArchitecture="neutral", un'applicazione di tipowin32Appe il nome dell'exe inserito inExecutable. -
Assets/— risorse visive segnaposto (estratte dall'icona del file EXE, quando possibile).
Perché una
sparse/cartella e non accanto all'exe? Il file manifesto eAssets/sono input in fase di compilazione usati dawinapp packewinapp embed-identity— nulla li legge accanto all'exe in fase di esecuzione (l'identità di runtime proviene dall'elemento<msix>incorporato nell'exe, oltre che dalla posizione esterna del pacchetto registrato, e il manifesto fa riferimento all'exe in base al nome, quindi la sua posizione è indipendente da dove si trova l'exe). Scriverli in una cartella dedicata controllata dal codice sorgente li mantiene fuori da una directory di output di compilazione (ad esempiobin/) che una pulizia/ricompilazione cancella e mantiene la cartella libera dai file binari in modo che i passaggi successivi rimangano puliti.winapp packewinapp embed-identitycercano automaticamente insparse/, quindi raramente è necessario specificare il percorso.
Nota: Il flusso di init sparse ignora deliberatamente tutte le installazioni di SDK/pacchetto : i pacchetti di sola identità non hanno dipendenze SDK.
Se nella directory di destinazione esiste già un oggetto appxmanifest.xml , init si arresta anziché sovrascriverlo (e il relativo Assets/). Eseguire di nuovo con --force per rigenerarlo.
Assicurarsi che Publisher nel manifesto generato corrisponda al certificato con cui si eseguirà l'accesso. Modificare appxmanifest.xml se necessario o passare --publisher durante la generazione.
Passaggio 2: Compilare e firmare il pacchetto di identità
Puntare winapp pack al manifesto di tipo sparse (un file, non una cartella):
winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx
Poiché il manifesto dichiara AllowExternalContent, winapp pack compila un'identità.msix contenente solo il manifesto, senza file binari, nessun asset. L'output è impostato su <PackageName>.identity.msix per impostazione predefinita nella directory corrente; usa --output per modificarlo. La firma avviene solo quando si passa --cert (o --generate-cert).
Passaggio 3: Incorporare l'identità nell'app
Incorporare l'elemento <msix> in modo che Windows connetta l'exe in esecuzione al pacchetto identity:
# EXE mode — modify the built binary in place (uses mt.exe)
winapp embed-identity ./bin/Release/net8.0-windows/MyApp.exe
In alternativa, mantenere il manifest side-by-side come file sottoposto a controllo della versione e ricompilare:
# XML mode — update an external SxS manifest, then rebuild your app
winapp embed-identity ./app.manifest
In modalità XML l'elemento <msix> viene inserito (o sostituito in) nel manifesto di destinazione. Fare riferimento al manifesto del progetto (per .NET, impostare <ApplicationManifest>app.manifest</ApplicationManifest>) e ricompilare in modo che l'elemento sia incorporato nell'exe.
Entrambe le modalità ricavano l'identità da una rappresentazione sparsa appxmanifest.xml. Quando si omette --manifest, winapp cerca prima in una cartella sparse/ (dove viene scritto per impostazione predefinita da winapp init --exe --sparse) accanto al file di destinazione, poi nella directory corrente, quindi ripiega sul file di destinazione e sulla directory corrente; specificare --manifest per indicare un altro percorso.
Nota: La modalità EXE riscrive il file binario con
mt.exe, che invalida qualsiasi firma Authenticode esistente. Firmare nuovamente l'exe (ad esempiowinapp sign ./MyApp.exe <cert.pfx>) prima di distribuirlo.
Passaggio 4 - Registrarsi (per i test locali)
I loghi del manifest vengono recuperati da external location in fase di esecuzione, non dall'elemento di sola identità .msix. Il passaggio 1 li ha scritti in ./sparse/Assets, quindi copiarli accanto al file exe (la posizione esterna) prima della registrazione; in caso contrario, Windows registra un layout mancante ogni logo a cui fa riferimento il 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
Registrate quindi il pacchetto di identità in quella cartella (il percorso esterno):
Add-AppxPackage -Path .\MyApp.identity.msix `
-ExternalLocation (Resolve-Path .\bin\Release\net8.0-windows)
Avvia l'app e verifica che l'identità sia presente — ad esempio, Windows.ApplicationModel.Package.Current.Id.FamilyName dovrebbe restituire il nome della famiglia di pacchetti anziché generare un'eccezione.
Per eseguire la pulizia:
Remove-AppxPackage <full-package-name>
Gestione degli asset
Il tipo sparse .msix è di tipo identity-only. Le risorse visive a cui fa riferimento il manifest (Assets\StoreLogo.png, riquadri e così via) vengono individuate nel percorso del contenuto esterno in fase di esecuzione, cioè nella directory di installazione dell'app, e non all'interno di .msix.
Ciò significa che è necessario distribuire la Assets/ cartella insieme all'applicazione (lo stesso layout previsto dal manifesto, rispetto al percorso esterno).
Il passaggio 2 crea direttamente il pacchetto dal file manifest (winapp pack ./sparse/appxmanifest.xml), generando il .msix contenente solo l'identità a partire unicamente da quel manifest: i file presenti nella stessa directory vengono ignorati, quindi non include mai le tue risorse o i file binari. (Se invece si fa puntare winapp pack a una cartella il cui file manifesto dichiara AllowExternalContent, vengono segnalati eventuali asset o file binari trovati, poiché in un pacchetto sparse questi devono trovarsi nel percorso esterno, non all'interno di .msix.)
Integrazione del programma di installazione
La registrazione e l'annullamento della registrazione sono il processo del programma di installazione. Il modello è lo stesso tra gli strumenti del programma di installazione:
-
Installa: copiare i file binari dell'app, la
Assets/cartella e l'oggetto.msixnella directory di installazione, quindi eseguireAdd-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>". -
Disinstallazione: eseguire
Remove-AppxPackage <full-package-name>prima di eliminare i file.
Sicurezza: la directory di installazione viene determinata al momento dell'installazione e può contenere caratteri (ad esempio un apostrofo) che interrompono una stringa letterale di PowerShell. Effettuate sempre l'escape del percorso o convalidatelo prima di interpolarlo in una stringa
-Command: i frammenti di codice WiX e NSIS riportati di seguito presuppongono un percorso di installazione attendibile, mentre l'esempio di Inno Setup mostra come effettuare correttamente l'escape. Preferire il passaggio dei percorsi come argomenti a uno script-Filerispetto all'interpolazione in linea-Command.
Installazione di Inno
Compilare gli argomenti di PowerShell in una [Code] funzione in modo che il percorso di installazione del runtime venga preceduto da un carattere di escape per il valore letterale powerShell tra virgolette singole (una directory di installazione contenente un elemento ' non deve essere in grado di inserire uno 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;
Per un esempio completo e funzionantesetup.iss, vedere l'esempio di app sparse.
Gli esempi di WiX e NSIS riportati di seguito richiamano un piccolo register-sparse.ps1 tramite -File in modo che il percorso di installazione venga passato come parametro (PowerShell lo associa come dati) anziché essere interpolato in una -Command stringa. In questo modo si evita l'inserimento di script tramite una directory di installazione creata (ad esempio, un nome di cartella contenente un virgolette o $(...)):
# 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)
Registrare per utente (Impersonate="yes"), perché Add-AppxPackage registra il pacchetto per l'account che lo esegue. Un'azione posticipata con Impersonate="no" viene eseguita come LocalSystem, che non concede l'identità all'utente di installazione (e viene comunemente rifiutata). Per un MSI per computer, eseguire la registrazione tramite impersonificazione in modo che venga applicata all'utente chiamante.
Un'azione personalizzata posticipata non può leggere INSTALLFOLDER direttamente (azioni posticipate eseguite in un contesto senza accesso alle proprietà) e dichiarare semplicemente che l'azione non viene eseguita. Quindi, convoglia i percorsi tramite CustomActionData, un'azione immediata di tipo 51 il cui Property nome è uguale a quello dell'azione Id differita, e pianifica entrambi dopo 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 è incluso nell'estensione util di WiX (WixUtilExtension); fai riferimento a essa in modo che il file binario WixCA sia disponibile.
Una singola azione rappresentata registra l'identità solo per l'utente che esegue il programma di installazione. Per effettuare il provisioning di ogni utente di un'installazione per macchina, eseguire invece la registrazione al primo avvio (per utente) oppure usare un meccanismo di provisioning come
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
Risoluzione dei problemi
Package.Current genera l'errore "no package identity" in fase di esecuzione
- Il pacchetto di identità non è registrato oppure nel manifesto Fusion del file EXE manca l'elemento
<msix>.winapp embed-identityEseguire di nuovo (e ricompilare se si usa la modalità XML), quindi ripetere la registrazione conAdd-AppxPackage -ExternalLocation. - L'oggetto
<msix packageName>/applicationId/publishernell'exe deve corrispondere esattamente all'identità del pacchetto registrato.
Gli asset o i logo non vengono visualizzati
- Assicurati che la cartella
Assets/sia presente nel percorso esterno con gli stessi percorsi relativi attesi dal manifest. Gli asset vengono risolti dalla posizione esterna, non dall'oggetto.msix.
Add-AppxPackage ha esito negativo con un errore di firma/attendibilità
-
.msixdeve essere firmato da un certificato considerato attendibile nel computer e il cui soggetto corrisponde alPublisherdel manifest. Per i test locali, generare e considerare attendibile un certificato di sviluppo conwinapp cert generatee assicurarsi che il manifestoPublishercorrisponda.
MakeAppx: "L'applicazione con valore RuntimeBehavior 'win32App' non deve dichiarare EntryPoint"
- Un'applicazione di tipo sparse
win32Appnon deve dichiarareEntryPoint. I manifesti generati dawinapp init --sparsesono già corretti. Rimuovere qualsiasiEntryPointattributo se il manifesto è stato modificato a mano.
"L'input è un file ma non un manifest sparse"
-
winapp pack <file>accetta solo un manifesto che dichiara<uap10:AllowExternalContent>true</uap10:AllowExternalContent>. Generarne uno conwinapp init --exe <exe> --sparseo passare una cartella di input per compilare un file MSIX completo.