Skip to content

Empaquetar una herramienta de escritorio Python como MSIX para Microsoft Store

2026-04-27

Tags: Tutorial · Python · Windows · MSIX · Empaquetado

Introducción

En un artículo anterior, escribí sobre la motivación y el proceso de desarrollo de RDP Heartbeat. Al final de ese artículo mencioné:

Tengo planeado escribir un artículo detallando todo el proceso de empaquetado de Python a MSIX cuando tenga tiempo.

Aquí está.

Sinceramente, llevar una herramienta de escritorio escrita en Python al Microsoft Store es mucho más complicado que con C# / WinUI 3. Con estos últimos, Visual Studio lo gestiona con un par de clics. Pero si eres como yo y usas Python, el camino es este:

Código fuente Python → PyInstaller → EXE → Inno Setup → Instalador → MSIX Packaging Tool → MSIX → Store

Cada paso puede tener obstáculos, pero cada uno también tiene soluciones. Este artículo es el mapa y la guía.

Este artículo usa el proyecto RDP Heartbeat como ejemplo, pero los métodos y enfoques se aplican a cualquier herramienta Windows escrita en Python + Tkinter (o PyQt, wxPython, etc.).

Una nota: teóricamente, es posible convertir directamente un exe generado por PyInstaller con el MSIX Packaging Tool, omitiendo Inno Setup. Elegimos pasar primero por Inno Setup principalmente para facilitar la distribución propia (por ejemplo, poner un Setup.exe en GitHub Releases para que la gente lo descargue). Puede que no sea la solución óptima en teoría, pero es la que realmente nos funcionó.

Código fuente de referencia: El código completo del proyecto se puede consultar en [email protected], incluyendo build_release.py, setup.iss y todas las configuraciones de empaquetado.

Vista general: El pipeline de cuatro pasos

Antes de empezar, veamos toda la cadena:

  • PyInstaller: .py.exe
  • Inno Setup: .exe → Instalador
  • MSIX Packaging Tool: Instalador → .msix
  • Microsoft Store: Publicación

¿Por qué tan complicado? Porque el Microsoft Store necesita un paquete en formato MSIX, y el MSIX Packaging Tool funciona «convirtiendo» un instalador tradicional que ya instala y funciona correctamente — supervisa el proceso de instalación, captura todas las escrituras de archivos, cambios en el registro y creación de accesos directos, y luego genera automáticamente un MSIX. Por eso primero necesitas un instalador tradicional limpio y completo.

En otras palabras: PyInstaller se encarga de «que funcione», Inno Setup de «que se instale», y el MSIX Packaging Tool de «que se pueda publicar en el Store».

Vamos paso a paso.

Paso 1: PyInstaller — Convertir Python en EXE

1.1 Preparar tu proyecto

Antes de empezar, asegúrate de que tu proyecto Python tiene una estructura clara y un punto de entrada bien definido. Tomando RDP Heartbeat como ejemplo:

prune-rdp-heartbeat/
├── main.py              # Script de entrada
├── heartbeat_window.py  # Lógica de la ventana principal
├── win_utils.py         # Wrappers de API Win32
├── tray_icon.py         # Bandeja del sistema
├── settings_dialog.py   # Panel de configuración
├── about_dialog.py      # Diálogo Acerca de
├── config_manager.py    # Gestión de configuración
├── i18n.py              # Internacionalización
├── startup.py           # Inicio automático
├── logger.py            # Registro
├── version.py           # Número de versión
├── icon.ico             # Icono de la aplicación
├── icon.png             # Icono fuente (para generar recursos MSIX)
├── requirements.txt     # Dependencias
└── packaging/           # Recursos MSIX (se usarán más adelante)

Dependencias en requirements.txt:

pystray
Pillow
pywin32
customtkinter
packaging

1.2 Manejar las «dependencias ocultas» de PyInstaller

PyInstaller detecta dependencias mediante análisis estático, pero algunas bibliotecas (especialmente pystray, PIL) no se detectan y deben declararse manualmente:

python
# Configuración clave en build_release.py
cmd = [
    sys.executable, "-m", "PyInstaller",
    "--noconsole",           # Sin ventana de consola
    "--onefile",             # Empaquetar en un solo exe
    "--name=RDPHeartbeat",
    "--clean",
    "--icon=icon.ico",
    "--add-data=icon.ico;.", # Windows usa punto y coma, Linux/macOS dos puntos
    "--hidden-import=PIL._tkinter_finder",
    "--hidden-import=pystray",
    "main.py"
]

Además, los archivos de recursos (como iconos) se extraen a un directorio temporal después del empaquetado, por lo que necesitas resource_path() para compatibilidad:

python
# Enfoque en tray_icon.py
def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS   # Directorio temporal de PyInstaller
    except Exception:
        base_path = os.path.abspath(".")  # Modo desarrollo: directorio actual
    return os.path.join(base_path, relative_path)

Cualquier recurso que necesite leerse del sistema de archivos (iconos, fuentes, plantillas de configuración, etc.) debe pasar por esta función.

1.3 Script de build con un clic

Encapsula el comando PyInstaller en build_release.py:

python
# Primero leer version.py para obtener el número de versión
from version import APP_VERSION

def run_build():
    # 0. Limpiar artefactos del build anterior
    if os.path.exists("build"): shutil.rmtree("build")
    if os.path.exists("dist"):  shutil.rmtree("dist")

    # 1. Ejecutar PyInstaller
    subprocess.check_call([...])

    # 2. Verificar el resultado
    exe_path = os.path.join("dist", "RDPHeartbeat.exe")
    assert os.path.exists(exe_path), "Build failed!"

Ejecuta python build_release.py. Si todo va bien, obtendrás RDPHeartbeat.exe en dist/.

Puedes hacer doble clic para verificar que funciona correctamente.

Paso 2: Inno Setup — Convertir el EXE en un instalador formal

El MSIX Packaging Tool necesita un instalador como fuente de conversión. En el ecosistema Windows, Inno Setup es una herramienta gratuita, madura y basada en scripts para crear instaladores.

2.1 Instalar Inno Setup

Descarga e instala desde jrsoftware.org. Se recomienda instalar también Inno Script Studio (editor visual).

2.2 Escribir el script de instalación

El archivo principal setup.iss:

ini
#define MyAppName "RDP Heartbeat"
#define MyAppVersion "1.1.3.0"
#define MyAppPublisher "Prune Lab"
#define MyAppExeName "RDPHeartbeat.exe"

[Setup]
AppId={{DDEC247A-5DC0-4393-BB13-466CCAD7F90B}   ; GUID único por aplicación
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
DefaultDirName={autopf}\{#MyAppName}             ; Por defecto en Program Files
ArchitecturesAllowed=x64compatible                ; Sistemas de 64 bits
ArchitecturesInstallIn64BitMode=x64compatible
DisableProgramGroupPage=yes                       ; Omitir página «Seleccionar grupo de programas»
OutputBaseFilename=RDPHeartbeat_Setup             ; Nombre del archivo de salida
SolidCompression=yes
WizardStyle=modern                                ; Estilo de asistente moderno
SetupIconFile=icon.ico

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
Source: "dist\{#MyAppExeName}"; DestDir: "{app}"; Flags: ignoreversion

[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}";  Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; Flags: unchecked

[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "{cm:LaunchProgram,...}"; Flags: nowait postinstall skipifsilent

Algunos puntos importantes:

  • AppId debe ser único: es la base para que Windows identifique la aplicación. Evita conflictos con otro software. El IDE de Inno Setup puede generar un GUID con un clic.
  • No necesitas escribir en el registro: la conversión MSIX maneja automáticamente las escrituras en el registro durante la instalación, pero esta aplicación no las necesita de todos modos.

2.3 Sincronización automática del número de versión

Tu número de versión puede estar definido en el código Python (version.py), y también hay una copia en setup.iss. La sincronización manual tarde o temprano causará errores. Añade un parche automático en build_release.py:

python
def patch_setup_iss():
    with open("setup.iss", 'r', encoding='utf-8') as f:
        content = f.read()
    content = re.sub(
        r'#define MyAppVersion ".*?"',
        f'#define MyAppVersion "{APP_VERSION}"',
        content
    )
    with open("setup.iss", 'w', encoding='utf-8') as f:
        f.write(content)

Ahora, al ejecutar python build_release.py, la última versión se escribirá automáticamente en setup.iss y luego se ejecutará PyInstaller.

2.4 Compilar el instalador

Abre el IDE de Inno Setup, carga setup.iss y haz clic en Compile. El resultado es RDPHeartbeat_Setup.exe.

Verificación: Ejecuta el instalador en una VM limpia u otro ordenador y confirma:

  • Se instala correctamente
  • El programa se inicia
  • No quedan residuos después de desinstalar (el desinstalador de Inno Setup está en {app}\unins000.exe, MSIX lo incluirá en el paquete, no hay problema)

Paso 3: MSIX Packaging Tool — Del instalador al formato Store

Si planeas publicar en el Microsoft Store, se recomienda registrarse primero en Partner Center y reservar el nombre de la aplicación (ver Paso 4), ya que el Package Name y Publisher que se rellenan en MPT deben coincidir con la información reservada en el Store. Si solo estás haciendo pruebas, puedes omitir el registro y usar cualquier nombre.

3.1 Preparar el entorno

Necesitarás:

  • MSIX Packaging Tool (Documentación oficial)
  • Un entorno limpio (se recomienda usar una VM u otro ordenador, ya que MPT supervisa todo el proceso de instalación — otros programas en tu sistema podrían capturarse como ruido)
  • Certificado de firma: para pruebas, puedes usar un certificado autofirmado. Ejecuta en PowerShell (administrador):
powershell
New-SelfSignedCertificate -Type Custom -Subject "CN=YourName" -KeyUsage DigitalSignature -FriendlyName "Your Cert" -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")

Exportar el certificado:

powershell
$password = ConvertTo-SecureString -String "YourPassword" -Force -AsPlainText
Export-PfxCertificate -Cert "Cert:\CurrentUser\My\<Thumbprint>" -FilePath "C:\cert.pfx" -Password $password

Al publicar oficialmente, el Microsoft Store volverá a firmar con su propio certificado, por lo que no necesitas comprar uno. Pero si quieres distribuir fuera del Store (por ejemplo, ofrecer descarga de MSIX en tu propio sitio web), necesitarás un certificado de firma de código adecuado.

3.2 Ejecutar el MSIX Packaging Tool

  1. Abre MPT, selecciona "Application package""Create a new package".
  2. En el paso "Select environment", mantén los valores predeterminados y haz clic en Next.
  3. Paso clave: MPT te pedirá que selecciones la ruta del instalador. Elige el RDPHeartbeat_Setup.exe que acabas de generar.
  4. MPT empieza a supervisar el sistema y lanza el instalador. Completa la instalación normalmente — si el instalador tiene marcado "Launch RDP Heartbeat" al final, no hay problema.
  5. Una vez completada la instalación, haz clic en "Next" — MPT detiene la supervisión.
  6. MPT mostrará todos los procesos detectados. Desmarca los que no pertenezcan a tu aplicación (procesos del sistema, actualizaciones del antivirus, etc.). Normalmente solo conserva RDPHeartbeat.exe, unins000.exe, etc.
  7. Rellena la información de la aplicación:
    • Package name: Para el Store, usa el nombre asignado al reservar la aplicación en Partner Center (ej.: PruneLab.RDPHeartbeat). Para pruebas locales, puedes usar cualquier nombre — formato recomendado: YourName.YourApp.
    • Package display name: El nombre para mostrar de la aplicación, ej.: RDP Heartbeat.
    • Publisher display name: Nombre del editor, ej.: Prune Lab.
    • Publisher: Para el Store, usa el Subject del certificado Publisher de tu cuenta Partner Center (ej.: CN=21C32622-53AF-45A6-8AB1-B35BCD475CAD). Para pruebas locales, usa el Subject de tu certificado autofirmado (ej.: CN=YourName).
    • Version: Se extrae automáticamente del instalador.
  8. Especifica el directorio de salida y genera el paquete MSIX.

3.3 Probar el paquete MSIX

Después de generar el archivo .msix, no modifiques el manifest todavía. Haz doble clic para instalar y verifica que funciona correctamente. Si usas un certificado autofirmado, necesitas importarlo primero al almacén de «Personas de confianza» del sistema (ver el comando de exportación en la sección anterior, usa Import-PfxCertificate).

Paso 4: Enviar al Microsoft Store

4.1 Registro y reserva de nombre

  1. Regístrate para una cuenta de desarrollador en Microsoft Partner Center.
  2. En Partner Center, reserva el nombre de la aplicación (Products → New Product → introduce el nombre). Tras la reserva, obtendrás el Package Name y la información de Publisher, que deben coincidir con el AppxManifest.xml de tu paquete MSIX.

4.2 Proceso de envío

  1. Ve a tu aplicación → SubmissionsNew submission.
  2. Rellena los campos:
    • Packages: Sube el archivo .msix
    • Description: Descripción de la aplicación (soporta varios idiomas, se recomienda al menos inglés y chino)
    • Screenshots: Al menos una captura de pantalla 1366×768
    • Store listing: Iconos, imágenes promocionales, lista de funcionalidades
  3. Envía para revisión. La primera revisión suele tardar 1-3 días laborables.

Conclusión: ¿Merece la pena?

Si esperas ganar mucho dinero con una herramienta Python, la respuesta es «probablemente no». Reescribir con WinUI 3 / .NET daría una experiencia de empaquetado mucho mejor.

Pero si eres como yo — ya tienes una herramienta escrita en Python que usas regularmente y quieres compartirla con más gente — entonces este proceso, aunque laborioso, al menos funciona.

Repaso del pipeline:

EtapaHerramientaResultado
.py → .exePyInstallerUn solo exe
.exe → InstaladorInno SetupSetup.exe
Instalador → MSIXMSIX Packaging Tool.msix
EnvíoPartner CenterPublicación en el Store

Espero que este artículo te ayude a evitar algunos rodeos. Si también has llevado una herramienta Python al Store, no dudes en compartir tu experiencia en el GitHub de RDP Heartbeat.