Distribución de apps de Windows en un solo archivo - los límites del binario único y la dependencia del SO

· Actualizado el: · · Windows, Distribución, Binario único, .NET, C++, WebView2, WinUI

Este artículo nació de una charla informal sobre «hasta dónde se puede llamar binario único a algo en Windows». La publicación que lo originó es la siguiente. Aunque se omita esta parte, el resto está escrito para poder leerse de forma independiente.

En Windows, «si es posible, nos gustaría distribuirlo en un solo archivo» es una petición bastante habitual. Para herramientas internas, herramientas de integración con equipos, terminales de monitorización, entornos sin conexión y sitios que quieren evitar en lo posible el uso de instaladores, convertir la app en un binario único resulta muy atractivo.

Sin embargo, si no se separa este tema desde el principio, la conversación suele dejar de tener sentido a mitad de camino. Esto se debe a que, en Windows, «queremos un binario único» tiende a mezclar en realidad cuatro cuestiones distintas.

  • Queremos un único paquete de distribución
  • Queremos no tener que preinstalar los runtimes de .NET o Visual C++
  • Queremos que funcione con solo colocarlo, sin instalador ni permisos de administrador
  • No queremos depender de las diferencias entre versiones del Windows de destino

Estas cuatro cosas no son lo mismo. En la práctica, la forma de pensarlo que menos se desvía es esta.

Reducir el paquete de distribución a un solo EXE es bastante posible. Pero eliminar por completo la dependencia del Windows de destino no lo es.

En este artículo organizamos esa línea divisoria pensando en el trabajo práctico con apps de Windows.

1. Conclusión inicial

Si resumimos solo la conclusión al principio, es esta.

  • Con un EXE de escritorio normal, se puede llevar la conversión a binario único bastante lejos
  • Sin embargo, poder reducirlo a 1 EXE y no depender del Windows de destino son cosas distintas
  • En las extensiones de shell, los servicios de Windows, los controladores, WebView2 y una parte de WinUI 3, lo importante suele ser menos el número de archivos y más qué se registra en el SO y qué se da por sentado
  • Lo más importante en la práctica es decidir por separado si lo que se busca es el binario único, prescindir del instalador o reducir la dependencia del SO

Dicho de otro modo, la línea divisoria en Windows queda así.

  • Reducir el paquete de distribución a uno solo: bastante posible
  • Incluir runtimes adicionales: bastante posible
  • Acercarse a la distribución por xcopy: depende del tipo de app
  • Eliminar la dependencia del lado del Windows de destino: imposible

1.1 Terminología usada en este artículo

Antes de continuar, organizamos aquí los términos que aparecerán repetidamente más adelante.

Término Lectura o expansión Significado en este artículo
UCRT Universal C Runtime Parte de la biblioteca C estándar que resultó de dividir el runtime de C en Visual Studio 2015. Desde Windows 10 en adelante se incluye como componente del propio SO
Paquete redistribuible de VC++ Visual C++ Redistributable Instalador para incluir en el equipo de destino las DLL del runtime de Visual C++ distintas de UCRT
framework-dependent Distribución que depende del .NET del entorno de destino Forma de distribución que da por sentado que el equipo de destino ya tiene instalado el runtime de .NET
self-contained Runtime de .NET incluido Forma de distribución en la que la propia app incluye el conjunto completo del runtime de .NET
single-file Publicación en archivo único Opción de publicación de .NET que reúne el paquete de distribución en un solo EXE
Native AOT Native Ahead-Of-Time Forma de distribución que precompila el IL a código nativo en el momento de la publicación. No usa JIT en tiempo de ejecución
Distribución app-local Distribución adyacente a la app Forma de distribución en la que las DLL se colocan en la misma carpeta que el EXE y solo esa app las usa
Distribución xcopy Distribución de solo copiar Forma de distribución que funciona con solo colocar la carpeta, sin instalador ni permisos de administrador
SCM Service Control Manager, administrador de control de servicios Mecanismo del SO que gestiona el registro, inicio y detención de los servicios de Windows
UAC User Account Control, control de cuentas de usuario Mecanismo de seguridad de Windows que controla la elevación a permisos de administrador
Extensión de shell Extensión de shell Componente COM que se carga y se ejecuta dentro de procesos como el Explorador
Evergreen Modo de actualización continua Modo de distribución que deja el WebView2 Runtime a cargo de una única copia compartida que se actualiza automáticamente
Fixed Version Modo de versión fija Modo de distribución que incluye una versión específica del WebView2 Runtime junto con la propia app
arch architecture, arquitectura de CPU Se refiere a x86, x64 o Arm64

2. Cuatro niveles para pensar el binario único

Si primero representamos los niveles en un diagrama, queda así. La dificultad aumenta de abajo hacia arriba, y el nivel superior no se puede alcanzar en Windows.

Niveles de la conversión a binario único en WindowsMuestra los cuatro niveles, desde tener un solo paquete de distribución hasta no depender del Windows de destino, indicando cuáles son alcanzables y cuál noNivel A: el paquete de distribución es uno soloBastante posibleNivel B: no hace falta preinstalar el runtime del lenguajeBastante posibleNivel C: no hace falta instalación ni registroDepende del tipo de appNivel D: no depende del Windows de destinoInalcanzable en Windows

El trabajo necesario cambia por completo según a cuál de estos cuatro niveles se refiera realmente la frase «queremos un binario único».

2.1 Nivel A: el paquete de distribución es uno solo

Este es el aspecto más superficial.

  • Se puede enviar por correo como un único archivo
  • Basta con colocar un archivo en un USB
  • En el destino de instalación solo queda app.exe

Esto se refiere a la unidad de distribución tal como se ve. En la práctica, aunque se descomprima temporalmente al iniciarse o dependa de DLL del propio SO, esta condición por sí sola se puede cumplir.

2.2 Nivel B: no hace falta preinstalar el runtime del lenguaje

El siguiente nivel es el estado en el que la app funciona sin necesidad de instalar de antemano el runtime de .NET o el paquete redistribuible de VC++ en la máquina de destino.

  • Enlace estático de C/C++
  • self-contained de .NET
  • single-file de .NET
  • Native AOT de .NET

En este nivel, la sensación de «poder llevarlo de forma autónoma» se vuelve mucho más fuerte.

2.3 Nivel C: no hace falta instalación ni registro

A partir de aquí la dificultad aumenta de golpe.

Un EXE simple a veces funciona con solo colocarlo. Pero los siguientes casos son distintos.

  • Extensiones de shell
  • Servicios de Windows
  • Esquemas de URL personalizados o asociaciones de archivos
  • Controladores
  • Componentes que se cargan en otros procesos, como el Explorador o Office

En este terreno no basta con colocar el archivo. Hace falta registro por parte del SO y conexión con el host.

2.4 Nivel D: no depender del Windows de destino

Esto es imposible en Windows.

Se debe a que una app de Windows termina ejecutándose sobre las API de Windows, el cargador, el modelo de seguridad y la pila de dispositivos. La conversión a binario único solo llega hasta el ámbito de responsabilidad de la propia app. No implica llevarse consigo el propio SO.

3. Áreas donde resulta bastante fácil reducirlo a un solo EXE

También en Windows hay apps relativamente fáciles de reducir a un solo EXE.

  • Herramientas de escritorio que se ejecutan de forma independiente
  • Apps de negocio en las que el propio EXE contiene la interfaz y el procesamiento
  • Herramientas de comunicación, procesamiento de archivos, recopilación de registros, monitorización o control de equipos
  • Apps que no necesitan integración con hosts como el Explorador o Office
  • Interfaces que no dan por sentado un runtime web

En este tipo de apps hay muchos elementos fáciles de incluir en el propio cuerpo de la aplicación.

  • Código propio
  • Recursos
  • El manifiesto
  • La configuración predeterminada
  • Datos de plantilla
  • Algunas bibliotecas de terceros
  • El propio runtime del lenguaje

Además, aunque no se incorporen por completo las DLL dentro del EXE, la distribución app-local, que coloca las DLL junto al EXE, es una opción habitual y sólida en Windows. En la práctica,

  • Un único app.exe
  • O app.exe más varias DLL adyacentes
  • Pero sin necesidad de instalador ni de permisos de administrador, distribuible por xcopy

No es raro que esta forma resulte más fácil de mantener que comprimirlo todo a la fuerza en un solo EXE.

4. Dependencias de Windows que no desaparecen aunque sea un solo EXE

Si se piensa que «con un solo EXE ya no se depende del Windows de destino», aquí es donde se produce el error. En realidad, incluso reduciéndolo a 1 EXE quedan dependencias.

4.1 Dependencia de la versión del SO

Cada API de Windows tiene su propio SO mínimo compatible. También existen diferencias entre x64 y Arm64. Es decir, incluso con un único EXE,

  • si debe funcionar hasta Windows 10
  • si se da por sentado Windows 11
  • si también debe funcionar en Windows Server
  • a cuál de x86, x64 o Arm64 se dirige

son decisiones que hay que fijar desde el principio.

4.2 Dependencia de las DLL del sistema

Aunque uno crea haberlo reducido a 1 EXE, en tiempo de ejecución, como es natural, se siguen usando componentes proporcionados por el SO.

  • kernel32.dll
  • user32.dll
  • advapi32.dll
  • La infraestructura COM
  • La infraestructura de control de servicios

Todo esto forma parte del ámbito de responsabilidad de Windows.

4.3 Dependencia del modelo de seguridad

  • UAC
  • Las ACL de archivos
  • El administrador de control de servicios
  • El registro
  • La directiva de firma de controladores

La app no puede hacerse cargo de todo esto por sí sola.

4.4 Dependencia del host o del runtime

Si el diseño no consiste en un EXE independiente, sino que se apoya en algún host, las dependencias aumentan de golpe.

  • Usar WebView2: hace falta el WebView2 Runtime
  • Usar WinUI 3 / Windows App SDK: hace falta organizar el modo de distribución
  • Crear una extensión de shell: hace falta registrarla en el Explorador

En definitiva, la elección de la interfaz o de la integración se traduce a menudo directamente en la dificultad de la distribución.

5. Puntos de equilibrio realistas según la tecnología

5.1 C/C++ nativo

El C/C++ nativo está del lado con mayor libertad para la conversión a binario único. Existe margen para elegir el enlace estático, y si se trata de un EXE independiente, resulta bastante fácil acercarse a ese objetivo.

Sin embargo, más que meterlo todo por la fuerza en un solo archivo,

  • qué hacer con UCRT o con el runtime de VC++
  • si colocar las DLL de terceros como app-local
  • hasta qué punto limitar la CPU y el SO de destino

es lo que resulta más importante en la práctica.

Punto de partida: la diferencia entre /MT y /MD

En MSVC, lo que decide en la práctica si se incluye o no el runtime es esta opción del compilador.

Opción Qué se enlaza Qué se necesita en el equipo de destino
/MD Las bibliotecas de importación para DLL ucrt.lib y vcruntime.lib. En tiempo de ejecución se usan ucrtbase.dll y vcruntime<versión>.dll UCRT y el runtime de VC++
/MT Enlace estático de libucrt.lib, libvcruntime.lib y libcmt.lib Ningún runtime adicional

La forma mínima de probarlo es esta, desde el Developer Command Prompt.

cl /std:c++17 /EHsc /O2 /MT app.cpp /Fe:app.exe

Aquí hay tres puntos que conviene tener en cuenta.

  • UCRT pasó a ser un componente de Windows cuando el runtime de C se dividió en Visual Studio 2015, y desde Windows 10 en adelante se incluye como parte del SO. Si se apunta a versiones de Windows anteriores, hace falta redistribuirlo mediante vcredist.
  • No se recomienda compilar el lado de la DLL con /MT. Como el CRT enlazado estáticamente mantiene su estado confinado dentro de esa misma DLL, la reserva de memoria, la configuración regional y el comportamiento de _set_se_translator quedan desalineados entre el EXE y la DLL. Si se uniforma sin más criterio «el EXE en /MT, la DLL incluida también en /MT», se producen errores al reservar y liberar memoria a través de ese límite.
  • /clr y /MT no se pueden usar juntos. Si se mezcla C++/CLI, hay que optar por /MD.

5.2 .NET

.NET cuenta con single-file, self-contained y Native AOT, por lo que la unidad de distribución visible se puede reducir bastante.

Sin embargo, es necesario distinguir entre ellos.

  • framework-dependent: depende del .NET del entorno de destino
  • self-contained: incluye el runtime de .NET
  • single-file: reúne el paquete de distribución en uno solo
  • Native AOT: reduce aún más las dependencias en el arranque, pero también impone restricciones de funcionalidad

No es que «al ser single-file, se reduzca la dependencia del SO». Lo que se reduce es, principalmente, la dispersión del paquete de distribución de la app.

Punto de partida: los 3 patrones de dotnet publish

Si primero se quiere ejecutar y ver las diferencias, estos tres comandos son suficientes.

rem 1. framework-dependent + single-file: se necesita el runtime de .NET en el equipo de destino
dotnet publish -c Release -r win-x64 --self-contained false -p:PublishSingleFile=true

rem 2. self-contained + single-file: incluye el runtime de .NET
dotnet publish -c Release -r win-x64 --self-contained true -p:PublishSingleFile=true

rem 3. Native AOT: publicar después de escribir PublishAot en el csproj
dotnet publish -c Release -r win-x64

La recomendación oficial es escribir PublishSingleFile y PublishAot en el csproj en lugar de en la línea de comandos, porque así se activa el análisis de compatibilidad durante la compilación y se reducen los errores que solo se detectan después de publicar.

<PropertyGroup>
  <PublishSingleFile>true</PublishSingleFile>
  <PublishAot>true</PublishAot>
</PropertyGroup>

Tenga en cuenta que, si especifica RuntimeIdentifier, SelfContained pasa a ser true de forma predeterminada. Si desea framework-dependent, especifique explícitamente --self-contained false. Para publicar Native AOT en Windows hace falta la carga de trabajo Desarrollo para el escritorio con C++ de Visual Studio.

La contrapartida: reducirlo a uno solo no sale gratis

Si aquí solo se escribiera lo que «se puede hacer», se correría el riesgo de un error, así que enumeramos también la contrapartida.

El despliegue en tiempo de arranque de single-file

  • De forma predeterminada, solo se empaquetan las DLL administradas. Se cargan en memoria al iniciarse y no se extraen a una carpeta. Los binarios nativos del propio runtime permanecen como archivos separados.
  • Si además desea incluir los binarios nativos en el único archivo, use IncludeNativeLibrariesForSelfExtract; si desea extraer todo el contenido antes de ejecutarse, use IncludeAllContentForSelfExtract. En ambos casos el comportamiento pasa a ser «extraer y después iniciar», y en Windows se extrae bajo %TEMP%\.net. Con DOTNET_BUNDLE_EXTRACT_BASE_DIR se puede cambiar la ubicación, pero no la convierta en un lugar en el que puedan escribir usuarios o servicios con permisos distintos.
  • Si activa EnableCompressionInSingleFile, el EXE se vuelve bastante más pequeño, pero el arranque se ralentiza porque el contenido se descomprime en memoria al iniciarse. La propia documentación oficial indica que hay que medir tanto el tamaño como el coste de arranque antes de usarlo. El impacto varía mucho según la app.

Incompatibilidades de API en single-file

Al reducir el paquete de distribución a uno solo, el código que da por sentada una ruta de archivo se rompe de forma silenciosa.

API Comportamiento en single-file
Assembly.Location Devuelve una cadena vacía
Assembly.CodeBase PlatformNotSupportedException
Assembly.GetFile IOException
Module.Name Devuelve la cadena <Unknown>

Si necesita acceder a un archivo junto al EXE, recurra a AppContext.BaseDirectory; si necesita la ruta del ejecutable, use Environment.ProcessPath. Como algunas bibliotecas de terceros usan internamente estas API, tras convertir la app a single-file hace falta comprobar al menos una vez el arranque en una máquina real.

Restricciones de funcionalidad de Native AOT

A cambio de «arranque rápido y sin necesidad de runtime», deja de estar disponible lo siguiente.

  • La carga dinámica, como Assembly.LoadFile
  • La generación de código en tiempo de ejecución, como System.Reflection.Emit
  • C++/CLI
  • En Windows, el COM integrado (built-in COM interop)
  • Como el trimming es obligatorio, también se heredan sus restricciones
  • Al ser en esencia single-file, también se heredan las incompatibilidades de API mencionadas arriba
  • System.Linq.Expressions siempre funciona en modo intérprete, por lo que resulta más lento que el código generado en tiempo de ejecución

La plataforma de destino también queda fijada de antemano. En .NET 8, Windows admite x64 y Arm64, y a partir de .NET 9 se añadió x86. La idea de «uno solo con AnyCPU» no es viable desde el principio.

5.3 WebView2

Al adoptar WebView2, la dificultad del binario único cambia por completo. Aquí el tema central no es el número de EXE, sino cómo tratar el WebView2 Runtime.

Antes de «¿se puede reducir a 1 EXE?», hay preguntas que conviene plantearse primero.

  • Si se da por sentado que el runtime ya existe en el entorno
  • Si se usa Evergreen
  • Si se incluye una Fixed Version
  • Hasta dónde asumir la responsabilidad en una distribución sin conexión

La diferencia entre Evergreen y Fixed Version es, a grandes rasgos, esta.

  Evergreen Fixed Version
Quién actualiza Microsoft. Se actualiza automáticamente Usted mismo. Se sustituye junto con las actualizaciones de la app
Instancia en el equipo Una sola compartida por todas las apps Incluida individualmente por cada app
Tamaño de distribución Prácticamente no aumenta Supera los 250 MB
Forma de instalación El Bootstrapper pesa unos 2 MB y descarga lo necesario. Sin conexión, se incluye el Standalone Installer Se distribuye el conjunto de binarios ya extraídos junto con la app
Restricciones existentes Hay que comprobar antes del arranque si ya está instalado en el equipo No se puede ejecutar desde una ruta de red ni desde una ruta UNC

El Evergreen Runtime viene preinstalado como parte del SO en Windows 11. Sin embargo, todavía quedan equipos con Windows 10 que no lo tienen, por lo que la propia Microsoft recomienda que, aunque se elija Evergreen, conviene distribuir también el Runtime. Es decir, en cualquier caso hace falta un procedimiento en el que la app compruebe si está presente y lo instale si falta. Fixed Version, a cambio de «poder controlar uno mismo el momento de la actualización», añade varios cientos de MB al paquete de distribución. Al margen de la cuestión del EXE único, esto es algo que hay que decidir de antemano.

5.4 WinUI 3 / Windows App SDK

WinUI 3 también hace que los requisitos de distribución cambien en el momento en que se adopta. La elección de la tecnología de interfaz se convierte directamente en la elección del método de distribución.

En concreto, hay dos ejes que decidir.

  • packaging: packaged (MSIX), packaged que apunta a una ubicación externa, o unpackaged
  • runtime: framework-dependent o self-contained

Y, desde el punto de vista del binario único, lo que realmente importa es esta restricción de combinación.

  • PublishSingleFile solo se puede usar en apps de WinUI 3 que sean unpackaged y self-contained a la vez, y requiere Windows App SDK 1.5 o posterior.
  • En las apps packaged, y en las packaged que apuntan a una ubicación externa, no se puede usar PublishSingleFile.
  • Al pasar a unpackaged se pierde la package identity, por lo que dejan de estar disponibles las funciones de Windows que dan por sentada la package identity, como las notificaciones, las tareas en segundo plano, las asociaciones de archivos o las extensiones del menú contextual.

En definitiva, en WinUI 3, «reducirlo a 1 EXE» y «usar las funciones de extensión de Windows» chocan de frente. Si el binario único es la máxima prioridad, a menudo es más rápido revisar desde el principio la premisa de la tecnología de interfaz.

6. Áreas que por naturaleza requieren registro y dependencias

6.1 Extensiones de shell

Las extensiones de shell que se cargan en el Explorador son algo distinto de un simple «EXE que basta con colocar». Aquí lo importante no es el número de archivos, sino cómo se registran en el Explorador.

6.2 Servicios de Windows

Aunque el propio exe del servicio se pueda reducir a un solo archivo, la distribución es un problema distinto.

  • El registro en el SCM
  • Los permisos
  • La cuenta de inicio
  • La configuración de recuperación

son aspectos que hay que tener en cuenta. Es decir, en los servicios, el terreno que hay que trabajar a fondo es más «cómo instalarlo» que «reducirlo a 1 EXE».

6.3 Controladores (drivers)

En los controladores esto es todavía más claro. Solo se completan incluyendo el INF, la firma y el procedimiento de instalación, por lo que desde el principio les cuesta encajar en el terreno del binario único.

7. Tabla de decisión para uso práctico

Para hacerse una idea general, esta tabla resulta útil.

Lo que se quiere crear Grado de viabilidad de 1 EXE Qué conviene decidir primero
Herramienta Win32/C++ independiente Alto Enlace estático, SO/arquitectura de destino
Herramienta WinForms/WPF independiente Alto Idoneidad de self-contained, single-file y Native AOT
App de WinUI 3 / Windows App SDK Medio Modo de distribución, dependencias adicionales
Interfaz de escritorio basada en WebView2 Bajo a medio Método de distribución del Runtime
Extensiones de clic derecho o vista previa del Explorador Bajo Registro de COM/registro del sistema
Servicio de Windows Medio Registro en el SCM, permisos, procedimiento de actualización
App con controlador incluido Bajo INF, firma, instalación

Lo más importante de esta tabla es entender que el «número de binarios» y el «ámbito de responsabilidad de la distribución» son cosas distintas.

8. Qué decidir antes de diseñar la distribución

Si se quiere lograr con éxito la conversión a binario único, hay cosas que conviene decidir antes de la implementación.

8.1 Decidir qué es lo que se quiere reducir a uno solo

  • Si se quiere un único paquete de distribución
  • Si se quiere eliminar la preinstalación del runtime
  • Si se quiere prescindir del instalador
  • Si se quiere simplificar la actualización sin conexión

La tecnología que se elija depende de la respuesta a esto.

8.2 Fijar desde el principio la versión mínima de Windows y la arquitectura

Tanto single-file como Native AOT son, básicamente, específicos del SO y de la arquitectura. Si se avanza dejando esto ambiguo, con la idea de «en cualquier caso, un solo archivo», al final se atasca por falta de API o por incompatibilidades de runtime.

8.3 Documentar explícitamente qué se incluye y qué se deja a cargo de Windows

En la práctica, con solo redactar esta lista se reducen bastante los errores.

  • Lo que se incluye en la app
    • El exe principal
    • Las DLL propias
    • Las plantillas de configuración
    • El runtime self-contained
  • Lo que se deja a cargo de Windows
    • Las DLL del sistema
    • Las API del SO
    • El SCM, el registro y el Explorador
    • La infraestructura de controladores
  • Lo que se da por sentado aparte
    • WebView2 Runtime
    • VC++ Redistributable
    • Office/Excel
    • Controladores específicos

8.4 Si el binario único es prioritario, reduzca la integración con el host

Esto tiene un efecto considerable.

  • Prescindir de las extensiones de shell y usar un EXE normal
  • No convertirlo en servicio, y recurrir al Programador de tareas o al inicio explícito
  • Usar una interfaz nativa en lugar de WebView2
  • Mantener COM confinado dentro del propio proceso

En definitiva, cuanto más se reduzca el diseño que hace que el SO «cargue» o «registre» algo, más cerca se estará del binario único.

9. Resumen

La conversión a binario único en Windows es posible hasta un punto considerable. Sin embargo, a lo que se llega al final es a esta frase.

Se puede reducir la app a un solo EXE. Pero no se puede reducir a un solo EXE el propio Windows del que esa app depende.

A continuación se enumeran cinco puntos que conviene recordar en especial.

  • Con un EXE normal e independiente, se puede llevar bastante lejos la distribución en un solo archivo
  • El enlace estático de C/C++, el single-file de .NET y Native AOT son opciones sólidas
  • Sin embargo, la dependencia de la versión del SO, la arquitectura, las DLL del sistema y el modelo de seguridad no desaparece
  • En las extensiones de shell, los servicios, los controladores, WebView2 y una parte de WinUI 3, lo central pasa a ser el registro en el SO o los runtimes adicionales
  • El éxito del binario único depende de separar desde el principio «qué es lo que se quiere reducir a uno solo»

Si se da una prioridad muy alta al binario único, resulta muchísimo más fácil tener éxito si, ya desde la elección de la tecnología, se diseña reduciendo el grado de acoplamiento con el SO.

10. Referencias

Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.

Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.

El artículo está directamente relacionado con los siguientes servicios.

Desarrollo de aplicaciones para Windows

En la distribución de apps de Windows conviene diseñar desde el principio la conversión a single-file, la inclusión del runtime, la decisión de adoptar WebView2 o WinUI y la conveniencia de convertir la app en servicio, para reducir los retrocesos posteriores.

Preguntas frecuentes

Preguntas habituales en las consultas sobre el tema del artículo.

¿Se puede distribuir una app de Windows con un solo archivo EXE?
Si se trata de una herramienta de escritorio que se ejecuta de forma independiente, es posible llegar bastante lejos. Con el enlace estático de C/C++, el self-contained o single-file de .NET, o Native AOT, se puede reducir el paquete de distribución a un único EXE. Sin embargo, poder empaquetarlo en un EXE y no depender del Windows de destino son cosas distintas: la dependencia de la versión del SO, la arquitectura, las DLL del sistema y el modelo de seguridad no desaparece.
¿El single-file de .NET elimina la dependencia del SO?
No. Lo que reduce principalmente el single-file es la dispersión del paquete de distribución de la app, no la dependencia del SO. El framework-dependent depende del .NET instalado en el entorno de destino, el self-contained incluye el runtime de .NET, y Native AOT reduce aún más las dependencias en el arranque, aunque también impone restricciones de funcionalidad. Tanto single-file como Native AOT son, en esencia, específicos del SO y de la arquitectura, así que hay que fijar desde el principio la versión mínima de Windows compatible y la arquitectura.
¿Qué tipo de apps son difíciles de reducir a un solo EXE?
Las extensiones de shell, los servicios de Windows, los controladores, las interfaces basadas en WebView2 y una parte de WinUI 3. En estos casos, lo importante no es el número de archivos, sino qué se registra en el SO y qué runtimes adicionales se necesitan. Por ejemplo, una extensión de shell necesita registrarse en el Explorador, un servicio requiere diseñar el registro en el SCM, los permisos y la cuenta de inicio, y un controlador solo se completa incluyendo el INF y la firma, por lo que no basta con distribuirlo simplemente colocando el archivo. Con WebView2 hay que decidir primero cómo se va a distribuir el WebView2 Runtime.
¿Hay una forma mejor de distribuir la app que forzarla dentro de un solo EXE?
La distribución app-local, en la que las DLL se colocan junto al EXE, es una alternativa sólida. Aunque sea app.exe más varias DLL adyacentes, si se puede distribuir por xcopy sin necesidad de instalador ni de permisos de administrador, no es raro que resulte más fácil de mantener que comprimirlo todo a la fuerza en un solo EXE. Lo importante es separar desde el principio si lo que se quiere es un único paquete de distribución, eliminar la preinstalación del runtime, o prescindir del instalador.

Perfil del autor

Página de presentación del autor del artículo.

Go Komura

Representante de KomuraSoft LLC

Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.

Volver al blog