Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Para ver un ejemplo funcional de extremo a extremo (aplicación WPF + instalador de Inno Setup), vea el ejemplo sparse-app.
Un archivo ejecutable de escritorio estándar( creado con dotnet build, MSBuild, CMake o cualquier otra cadena de herramientas) no tiene ninguna identidad de paquete. Sin identidad, no puede usar muchas API de Windows modernas (notificaciones del sistema, tareas en segundo plano, destinos de recursos compartidos, tareas de inicio, API de datos de la aplicación, etc.).
El empaquetado disperso otorga identidad a una aplicación sin mover sus archivos binarios a un MSIX. Distribuyes un paquete solo de identidad.msix pequeño (solo un manifiesto) y lo registras junto con tu aplicación instalada de forma habitual usando una ubicación externa. Su .exe permanece exactamente donde lo coloca su instalador. Este es el equivalente en producción de winapp create-debug-identity, que es solo para depuración durante el desarrollo.
En esta guía se describen los tres pasos de la CLI que se asignan a los tres primeros pasos del flujo de trabajo oficial Conceder identidad a aplicaciones no empaquetadas :
| Paso | Comando | Result |
|---|---|---|
| 1. Creación del manifiesto de identidad | winapp init --exe <exe> --sparse |
sparse/appxmanifest.xml + sparse/Assets/ |
| 2. Compilar y firmar el paquete de identidad | winapp pack <appxmanifest.xml> --cert <pfx> |
<PackageName>.identity.msix |
| 3. Insertar identidad en la aplicación | winapp embed-identity <exe> |
elemento del manifiesto de fusión del archivo exe <msix> |
Los pasos 4 a 5 de los documentos (registrar o anular el registro del paquete) son responsabilidad del instalador ; consulte Integración del instalador.
Cuándo usar el empaquetado disperso
- Ya dispone de un instalador consolidado (Inno Setup, WiX, NSIS, MSI) y no quiere cambiar a MSIX para la distribución, pero necesita API de Windows que requieren una identidad.
- La aplicación debe instalarse en una ruta o con una estructura que MSIX no permite.
- Desea un cambio mínimo y aditivo: mantenga el flujo de instalación existente y agregue un
.msixpaso de registro.
Si empieza desde cero y puede distribuirse como MSIX, una aplicación totalmente empaquetada (winapp init + winapp pack <folder>) es más sencilla.
Prerequisites
- Windows 10, versión 2004 (compilación 19041) o posterior. Los paquetes dispersos se basan en
uap10:AllowExternalContent, que requiere la versión 19041 o posterior. -
CLI de winapp : instale a través de winget (o actualice si ya está instalado):
winget install Microsoft.WinApp --source winget -
Un certificado de firma de código de confianza para la máquina de destino. Para las pruebas locales, genere un certificado de desarrollo con
winapp cert generatey confíe en él. Los paquetes de producción deben estar firmados con un certificado cuyo asunto coincida con el manifiestoPublisher.
Walkthrough
En los ejemplos siguientes se supone que hay un ejecutable compilado en ./bin/Release/net8.0-windows/MyApp.exe.
Paso 1: Creación del manifiesto de identidad disperso
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse
Esto deduce el nombre del paquete, el publicador, la versión y la descripción del archivo exe (a través de su información de versión de archivo) y le pide que acepte o invalidelos. Agregue --use-defaults (o --no-prompt) para omitir las indicaciones en CI y --name / --publisher para invalidar valores específicos:
winapp init --exe ./bin/Release/net8.0-windows/MyApp.exe --sparse --use-defaults `
--name "Contoso.MyApp" --publisher "CN=Contoso"
De forma predeterminada, escribe lo siguiente en la carpeta específica sparse/ dentro del directorio actual (se puede anular con --output-dir):
-
appxmanifest.xml— un manifiesto simplificado con<uap10:AllowExternalContent>true</uap10:AllowExternalContent>(un elemento dentro de<Properties>),ProcessorArchitecture="neutral", una aplicaciónwin32Appy el nombre del ejecutable indicado enExecutable. -
Assets/— recursos visuales de sustitución (extraídos del icono del archivo EXE cuando sea posible).
¿Por qué una
sparse/carpeta y no junto al exe? El manifiesto yAssets/son entradas en tiempo de compilación consumidas porwinapp packywinapp embed-identity— nada los lee junto al exe en tiempo de ejecución (la identidad en tiempo de ejecución procede del<msix>elemento insertado en el exe más la ubicación externa del paquete registrado, y el manifiesto hace referencia al exe por nombre, por lo que su ubicación es independiente de dónde reside el exe). Escribirlos en una carpeta dedicada controlada por código fuente las mantiene fuera de un directorio de salida de compilación (comobin/) que borraría una limpieza o recompilación y mantiene la carpeta libre de archivos binarios para que los pasos siguientes permanezcan limpios.winapp packywinapp embed-identitybuscan automáticamente ensparse/, por lo que rara vez es necesario especificar la ruta.
Nota: El flujo de inicialización disperso omite deliberadamente toda la instalación del SDK o paquete: los paquetes de solo identidad no tienen dependencias del SDK.
Si ya existe un appxmanifest.xml en el directorio de destino, init se detiene en lugar de sobrescribirlo (y su Assets/). Vuelva a ejecutar con --force para regenerarlo.
Asegúrese de que el Publisher del manifiesto generado coincida con el certificado con el que firmará. Edite appxmanifest.xml si es necesario o pase --publisher al generar.
Paso 2: Compilación y firma del paquete de identidad
Apunte winapp pack al manifiesto disperso (un archivo, no una carpeta):
winapp pack ./sparse/appxmanifest.xml --cert ./devcert.pfx
Dado que el manifiesto declara AllowExternalContent, winapp pack crea una identidad que solo.msix contiene el manifiesto, sin archivos binarios, sin recursos. La salida tiene <PackageName>.identity.msix como valor predeterminado en el directorio actual; use --output para cambiarla. La firma solo se produce cuando se pasa --cert (o --generate-cert).
Paso 3: Inserción de identidades en la aplicación
Inserte el <msix> elemento para que Windows conecte el exe en ejecución al paquete de identidad:
# EXE mode — modify the built binary in place (uses mt.exe)
winapp embed-identity ./bin/Release/net8.0-windows/MyApp.exe
Para mantener el manifiesto en paralelo como un archivo registrado y volver a compilar:
# XML mode — update an external SxS manifest, then rebuild your app
winapp embed-identity ./app.manifest
En el modo XML, el elemento <msix> se inserta en el manifiesto de destino (o se sustituye en él). Haga referencia a ese manifiesto del proyecto (para .NET, establezca <ApplicationManifest>app.manifest</ApplicationManifest>) y recompile para que el elemento se inserte en el exe.
Ambos modos leen la información de identidad desde un appxmanifest.xml disperso. Cuando se omite --manifest, winapp busca primero en la carpeta sparse/ (donde winapp init --exe --sparse la crea por defecto) junto al destino, después en el directorio actual y, como alternativa, recurre a buscar junto al destino y en el directorio actual; use --manifest para indicar otra ubicación.
Nota: El modo EXE vuelve a escribir el binario con
mt.exe, que invalida cualquier firma Authenticode existente. Vuelva a firmar el exe (por ejemplowinapp sign ./MyApp.exe <cert.pfx>, ) antes de distribuirlo.
Paso 4: Registrarse (para pruebas locales)
Los logotipos del manifiesto se cargan desde la ubicación externa en tiempo de ejecución, no desde el elemento solo de identidad .msix. El paso 1 los guardó en ./sparse/Assets, así que cópielos junto a su ejecutable (la ubicación externa) antes de registrarlo; de lo contrario, Windows registra una distribución a la que le faltan todos los logotipos a los que hace referencia el manifiesto:
# Copy the generated assets into the external location (beside your exe)
Copy-Item ./sparse/Assets -Destination .\bin\Release\net8.0-windows\Assets -Recurse -Force
A continuación, registre el paquete de identidad en esa carpeta (la ubicación externa):
Add-AppxPackage -Path .\MyApp.identity.msix `
-ExternalLocation (Resolve-Path .\bin\Release\net8.0-windows)
Inicie la aplicación y confirme que la identidad está presente; por ejemplo, Windows.ApplicationModel.Package.Current.Id.FamilyName debería devolver el nombre de familia del paquete en lugar de generar una excepción.
Para realizar una limpieza:
Remove-AppxPackage <full-package-name>
Control de recursos
El disperso .msix es solo identidad. Los recursos visuales a los que hace referencia el manifiesto (Assets\StoreLogo.png, iconos, etc.) se resuelven desde la ubicación de contenido externo en tiempo de ejecución( es decir, desde el directorio de instalación de la aplicación, no desde dentro de .msix.
Esto significa que debe desplegar la carpeta Assets/ junto a su aplicación (la misma estructura que espera el manifiesto, respecto a la ubicación externa).
El paso 2 empaqueta directamente el archivo de manifiesto (winapp pack ./sparse/appxmanifest.xml), que compila la identidad solo .msix a partir de ese manifiesto: los archivos del mismo nivel se omiten, por lo que nunca incluye los recursos o archivos binarios. (Si en su lugar apunta winapp pack a una carpeta cuyo manifiesto declara AllowExternalContent, advierte de cualquier recurso o archivo binario que encuentre, ya que, en un paquete disperso, estos pertenecen a la ubicación externa, no dentro de .msix).
Integración del instalador
El registro y la anulación del registro son el trabajo del instalador. El patrón es el mismo en todas las herramientas del instalador:
-
Instalación: copie los binarios de la aplicación, la carpeta
Assets/y.msixen el directorio de instalación y, a continuación, ejecuteAdd-AppxPackage -Path "<install-dir>\MyApp.identity.msix" -ExternalLocation "<install-dir>". -
Desinstalar: ejecute
Remove-AppxPackage <full-package-name>antes de eliminar archivos.
Seguridad: el directorio de instalación se resuelve en tiempo de instalación y puede contener caracteres (por ejemplo, una sola comilla) que se desglosan en un literal de cadena de PowerShell. Escape siempre o valide la ruta de acceso antes de interpolarla en una cadena
-Command: los fragmentos de código de WiX y NSIS que aparecen a continuación asumen una ruta de instalación de confianza, mientras que el ejemplo de Inno Setup muestra cómo realizar el escape de forma segura. Prefiere pasar rutas como argumentos a un script-Fileen lugar de la interpolación en línea-Command.
Configuración de Inno
Construya los argumentos de PowerShell en una función [Code] para que la ruta de instalación del entorno de ejecución quede escapada para el literal de PowerShell entre comillas simples (un directorio de instalación que contiene un ' no debe poder inyectar un 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 el ejemplo sparse-app para ver una setup.iss completa y funcional.
Los ejemplos de WiX y NSIS que aparecen a continuación invocan un pequeño register-sparse.ps1 mediante -File para que la ruta de instalación se pase como parámetro (PowerShell lo trata como datos) en lugar de interpolarse en una cadena -Command. Esto evita la inserción de scripts a través de un directorio de instalación diseñado (por ejemplo, un nombre de carpeta que contiene una comilla 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)
Regístrese por usuario (Impersonate="yes"), porque Add-AppxPackage registra el paquete para la cuenta que la ejecuta. Una acción diferida con Impersonate="no" se ejecuta como LocalSystem, que no otorga identidad al usuario que realiza la instalación (y normalmente se rechaza). Para una MSI por máquina, ejecute el registro suplantado para que se aplique al usuario que invoca.
Una acción personalizada diferida no puede leer INSTALLFOLDER directamente (las acciones diferidas se ejecutan en un contexto sin acceso a las propiedades), y simplemente declarar la acción no la ejecuta. Por lo tanto, encauza las rutas a través de CustomActionData mediante una acción inmediata de tipo 51 cuyo Property nombre sea igual al de la acción diferida Id y programa ambas después 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 se incluye en la extensión de utilidades de WiX (WixUtilExtension); haga referencia a ella para que el binario WixCA esté disponible.
Una sola acción con suplantación de identidad registra la identidad únicamente para el usuario que ejecuta el instalador. Para aprovisionar todos los usuarios de una instalación por máquina, regístrese en el primer inicio (por usuario) en su lugar o use un mecanismo de aprovisionamiento 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
Solución de problemas
Package.Current genera / «no package identity» en tiempo de ejecución
- El paquete de identidad no está registrado, o falta el elemento
<msix>en el manifiesto de fusión del exe. Vuelva a ejecutarwinapp embed-identity(y recompile si usa el modo XML), vuelva a registrarse conAdd-AppxPackage -ExternalLocation. - El
<msix packageName>/applicationId/publisherdel exe debe coincidir exactamente con la identidad del paquete registrado.
Los activos o logotipos no aparecen
- Asegúrese de que la carpeta
Assets/se implemente en la ubicación externa con las mismas rutas relativas que el manifiesto espera. Los recursos se resuelven desde la ubicación externa, no desde.msix.
Add-AppxPackage falla con un error de firma o de confianza
- El
.msixdebe estar firmado por un certificado que sea de confianza en la máquina y cuyo asunto coincida con el manifiestoPublisher. Para las pruebas locales, genere y confíe en un certificado de desarrollo conwinapp cert generatey asegúrese de que el manifiestoPublishercoincide con él.
MakeAppx: "La aplicación con el valor runtimeBehavior 'win32App' no debe declarar EntryPoint"
- Una aplicación dispersa
win32Appno debe declararEntryPoint. Los manifiestos generados porwinapp init --sparseya son correctos; quite cualquier atributoEntryPointsi ha editado manualmente el manifiesto.
"La entrada es un archivo pero no un manifiesto disperso"
-
winapp pack <file>solo acepta un manifiesto que declara<uap10:AllowExternalContent>true</uap10:AllowExternalContent>. Genere uno conwinapp init --exe <exe> --sparse, o pase una carpeta como entrada para compilar un MSIX completo.