Uso de la CLI de Winapp con C++ y CMake

En esta guía se muestra cómo usar la winapp CLI con una aplicación de C++ para depurar con la identidad del paquete y empaquetar la aplicación como MSIX.

La identidad del paquete es un concepto básico en el modelo de Windows app. Permite a la aplicación acceder a api de Windows específicas (como notificaciones, seguridad, API de IA, etc.), tener una experiencia de instalación o desinstalación limpia, etc.

Un archivo ejecutable estándar (como uno creado con cmake --build) no tiene la identidad del paquete. Esta guía muestra cómo agregarlo para depuración y luego empaquetarlo para su distribución.

Prerrequisitos

  1. Herramientas de compilación: use una cadena de herramientas del compilador compatible con CMake. En este ejemplo se usa Visual Studio. Puede instalar la edición comunitaria (o actualizarla si ya está instalada):

    winget install --id Microsoft.VisualStudio.Community --source winget --override "--add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --passive --wait"
    

    Reinicie después de la instalación.

  2. CMake: instale CMake (o actualice si ya está instalado):

    winget install Kitware.CMake --source winget
    
  3. CLI de winapp: instale la winapp cli mediante winget (o actualice si ya está instalada):

    winget install Microsoft.winappcli --source winget
    

1. Crear una nueva aplicación de C++

Empiece por crear una aplicación sencilla de C++. Crear un nuevo directorio para el proyecto.

mkdir cpp-app
cd cpp-app

Cree un archivo main.cpp con un programa básico "Hello, world!".

#include <iostream>

int main() {
    std::cout << "Hello, world!" << std::endl;
    return 0;
}

Cree un CMakeLists.txt archivo para configurar la compilación:

cmake_minimum_required(VERSION 3.20)
project(cpp-app)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

add_executable(cpp-app main.cpp)

Compile y ejecútelo para asegurarse de que todo funciona:

cmake -B build
cmake --build build --config Debug
.\build\Debug\cpp-app.exe

La salida debe ser "Hello, world!"

2. Actualizar código para comprobar la identidad

Actualizaremos la aplicación para comprobar si se ejecuta con la identidad del paquete. Esto nos ayudará a comprobar que la identidad funciona correctamente en pasos posteriores. Usaremos la API de Windows Runtime de C++ para acceder a las API de paquete.

En primer lugar, agregue la siguiente línea al final de la CMakeLists.txt para vincularla con la biblioteca modelo de Aplicación de Windows:

# Link Windows Runtime libraries
target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib)

A continuación, reemplace todo el contenido de main.cpp por el código siguiente. Este código intenta recuperar la identidad del paquete actual mediante la API de Windows Runtime. Si se realiza correctamente, imprime el nombre de familia del paquete; de lo contrario, imprime "No empaquetado".

#include <iostream>
#include <windows.h>
#include <appmodel.h>

int main() {
    UINT32 length = 0;
    LONG result = GetCurrentPackageFamilyName(&length, nullptr);
    
    if (result == ERROR_INSUFFICIENT_BUFFER) {
        // We have a package identity
        std::wstring familyName;
        familyName.resize(length);
        
        result = GetCurrentPackageFamilyName(&length, familyName.data());
        
        if (result == ERROR_SUCCESS) {
            std::wcout << L"Package Family Name: " << familyName.c_str() << std::endl;
        } else {
            std::wcout << L"Error retrieving Package Family Name" << std::endl;
        }
    } else {
        // No package identity
        std::cout << "Not packaged" << std::endl;
    }

    return 0;
}

3. Ejecutar sin identidad

Ahora, recompile y ejecute la aplicación como de costumbre:

cmake --build build --config Debug
.\build\Debug\cpp-app.exe

Debería ver el resultado "No empaquetado". Esto confirma que el ejecutable estándar se está ejecutando sin ninguna identidad de paquete.

4. Inicializar Project con la CLI de winapp

El comando winapp init configura todo lo que necesita de una vez: manifiesto de la aplicación, recursos y, opcionalmente, encabezados de SDK de Aplicaciones para Windows para el desarrollo de C++.

Ejecute el siguiente comando y siga las indicaciones:

winapp init .

Cuando se le solicite,

  • Nombre del paquete: presione Entrar para aceptar el valor predeterminado (cpp-app)
  • Nombre del editor: Presione Intro para aceptar el valor predeterminado o escriba su nombre.
  • Versión: presione Entrar para aceptar 1.0.0.0.
  • Punto de entrada: presione Entrar para aceptar el valor predeterminado (cpp-app.exe)
  • Setup SDK: seleccione "SDK estables" para descargar SDK de Aplicaciones para Windows y generar encabezados de C++

Este comando hará lo siguiente:

  • Crear Package.appxmanifest : el manifiesto que define la identidad de la aplicación
  • Crear la carpeta Assets — iconos necesarios para el empaquetado MSIX y la publicación en la Tienda
  • Crear una carpeta .winapp con encabezados y bibliotecas del SDK de Aplicaciones para Windows
  • Creación de un winapp.yaml archivo de configuración para anclar versiones del SDK

Puede abrir Package.appxmanifest para personalizar aún más las propiedades, como el nombre para mostrar, el publicador y las funcionalidades.

Agregar alias de ejecución (para aplicaciones de consola)

Un alias de ejecución permite a los usuarios ejecutar la aplicación por su nombre desde cualquier terminal (como cpp-app). También permite winapp run --with-alias durante el desarrollo, lo que mantiene la salida de la consola en el terminal actual en lugar de abrir una nueva ventana.

Puede agregar una automáticamente:

winapp manifest add-alias

O manualmente: abra Package.appxmanifest y, si falta, agregue el espacio de nombres uap5 a la etiqueta <Package>, y luego agregue la extensión dentro de <Applications><Application><Extensions>...:

<Package
  ...
  xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
+ xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5"
  IgnorableNamespaces="uap uap2 uap3 rescap desktop desktop6 uap10">

  ...
  <Applications>
    <Application ...>
      ...
+     <Extensions>
+       <uap5:Extension Category="windows.appExecutionAlias">
+         <uap5:AppExecutionAlias>
+           <uap5:ExecutionAlias Alias="cpp-app.exe" />
+         </uap5:AppExecutionAlias>
+       </uap5:Extension>
+     </Extensions>
    </Application>
  </Applications>
</Package>

5. Depurar con identidad

Para probar las características que requieren identidad (como notificaciones) sin empaquetar completamente la aplicación, puede usar winapp run. Esto registra un paquete de diseño flexible (al igual que una instalación MSIX real) e inicia la aplicación en un solo paso. No se necesita ningún certificado ni firma para la depuración.

  1. Compile el archivo ejecutable:

    cmake --build build --config Debug
    
  2. Ejecute con identidad:

    winapp run .\build\Debug --with-alias
    

La --with-alias marca inicia la aplicación a través de su alias de ejecución para que la salida de la consola permanezca en el terminal actual. Esto requiere el uap5:ExecutionAlias que agregamos en el paso 4.

Sugerencia

winapp run también registra el paquete en el sistema. Este es el motivo por el que MSIX puede aparecer como "ya instalado" al intentar instalarlo más adelante en el paso 8. Use winapp unregister para limpiar los paquetes de desarrollo cuando haya terminado.

Ahora debería ver una salida similar a la siguiente:

Package Family Name: cpp-app_12345abcde

Esto confirma que la aplicación se está ejecutando con una identidad de paquete válida.

Alternativa: identidad del paquete disperso

Si necesita un comportamiento de paquete disperso específicamente (identidad sin copiar archivos), puede usar create-debug-identity en su lugar:

winapp create-debug-identity .\build\Debug\cpp-app.exe
.\build\Debug\cpp-app.exe

Sugerencia

Para obtener flujos de trabajo de depuración avanzados (adjuntar depuradores, configuración del IDE, depuración de inicio), consulte la Guía de depuración.

6. Usar SDK de Aplicaciones para Windows (opcional)

Si ha seleccionado configurar los SDK durante winapp init, ahora tiene acceso a los encabezados de SDK de Aplicaciones para Windows en la carpeta .winapp/include. Esto le proporciona acceso a las API de Windows modernas, como notificaciones, ventanas, inteligencia artificial en el dispositivo, etc. Si solo necesita la identidad del paquete para la distribución, puede ir al paso 7.

Vamos a agregar un ejemplo sencillo que imprime la versión de Aplicación de Windows runtime.

Actualizar CMakeLists.txt

Agregue la siguiente línea al final del CMakeLists.txt para incluir los encabezados de SDK de Aplicaciones para Windows:

# Add Windows App SDK include directory
target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include)

Actualizar main.cpp

Reemplace todo el contenido de main.cpp para usar la API de tiempo de ejecución de aplicaciones de Windows.

#include <iostream>
#include <windows.h>
#include <appmodel.h>
#include <winrt/Microsoft.Windows.ApplicationModel.WindowsAppRuntime.h>

int main() {
    // Initialize WinRT
    winrt::init_apartment();
    
    UINT32 length = 0;
    LONG result = GetCurrentPackageFamilyName(&length, nullptr);
    
    if (result == ERROR_INSUFFICIENT_BUFFER) {
        // We have a package identity
        std::wstring familyName;
        familyName.resize(length);
        
        result = GetCurrentPackageFamilyName(&length, familyName.data());
        
        if (result == ERROR_SUCCESS) {
            std::wcout << L"Package Family Name: " << familyName.c_str() << std::endl;
            
            // Get Windows App Runtime version using the API
            auto runtimeVersion = winrt::Microsoft::Windows::ApplicationModel::WindowsAppRuntime::RuntimeInfo::AsString();
            std::wcout << L"Windows App Runtime Version: " << runtimeVersion.c_str() << std::endl;
        } else {
            std::wcout << L"Error retrieving Package Family Name" << std::endl;
        }
    } else {
        std::cout << "Not packaged" << std::endl;
    }
    
    return 0;
}

Compilación y ejecución

Vuelva a compilar la aplicación con los encabezados de SDK de Aplicaciones para Windows.

cmake --build build --config Debug
winapp run .\build\Debug --with-alias

Ahora debería ver un resultado similar a:

Package Family Name: cpp-app_12345abcde
Windows App Runtime Version: 1.8-stable (1.8.0)

El directorio />

  • winrt/: encabezados de proyección de C++ de WinRT para acceder a las API de Windows Runtime
  • Microsoft.UI.*.h - Encabezados WinUI 3 para componentes de interfaz de usuario modernos
  • MddBootstrap.h: arranque de SDK de Aplicaciones para Windows
  • WindowsAppSDK-VersionInfo.h - Información de versión
  • Y muchos más componentes de SDK de Aplicaciones para Windows

Para obtener un uso de SDK de Aplicaciones para Windows más avanzado, consulte la documentación de SDK de Aplicaciones para Windows.

7. Restaurar encabezados cuando sea necesario

La .winapp carpeta se agrega automáticamente a .gitignore mediante winapp init, por lo que no se comprobará en el control de código fuente. Cuando otros clonen el proyecto, tendrán que restaurar estos archivos antes de compilar.

Configuración manual

Ejecute estos dos comandos después de clonar el repositorio:

# Restore Windows App SDK headers
winapp restore

# Generate development certificate (optional - only if planning to package the app and sideload)
winapp cert generate --if-exists skip

A continuación, puede compilar y ejecutar normalmente con cmake -B build y cmake --build build --config Debug.

Configuración automatizada con CMake

Como alternativa, puede automatizar esto agregando lógica de configuración a CMakeLists.txt. Este es el completo CMakeLists.txt con la automatización, la vinculación adecuada y el estándar mínimo de C++20:

cmake_minimum_required(VERSION 3.20)
project(cpp-app)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# Download winapp CLI if not available in PATH
find_program(WINAPP_CLI winapp)
if(NOT WINAPP_CLI)
    set(WINAPP_DIR "${CMAKE_CURRENT_SOURCE_DIR}/.winapp-tools")
    set(WINAPP_CLI "${WINAPP_DIR}/winapp.exe")
    
    if(NOT EXISTS "${WINAPP_CLI}")
        message(STATUS "Downloading winapp CLI...")
        
        # Determine architecture
        if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|aarch64")
            set(WINAPP_ARCH "arm64")
        else()
            set(WINAPP_ARCH "x64")
        endif()
        
        # Download and extract
        set(WINAPP_ZIP "${CMAKE_CURRENT_BINARY_DIR}/winappcli.zip")
        file(DOWNLOAD 
            "https://github.com/microsoft/WinAppCli/releases/latest/download/winappcli-${WINAPP_ARCH}.zip"
            "${WINAPP_ZIP}"
            SHOW_PROGRESS
        )
        
        file(ARCHIVE_EXTRACT INPUT "${WINAPP_ZIP}" DESTINATION "${WINAPP_DIR}")
        file(REMOVE "${WINAPP_ZIP}")
        message(STATUS "winapp CLI downloaded to ${WINAPP_DIR}")
    endif()
endif()

# Automatically restore Windows App SDK headers and generate certificate if needed
# This runs once during CMake configuration, not on every build
if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include")
    message(STATUS "Restoring Windows App SDK headers...")
    execute_process(
        COMMAND "${WINAPP_CLI}" restore
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        RESULT_VARIABLE RESTORE_RESULT
    )
    if(NOT RESTORE_RESULT EQUAL 0)
        message(WARNING "Failed to restore Windows App SDK. Run 'winapp restore' manually.")
    endif()
endif()

if(NOT EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/devcert.pfx")
    message(STATUS "Generating development certificate...")
    execute_process(
        COMMAND "${WINAPP_CLI}" cert generate --if-exists skip
        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
        RESULT_VARIABLE CERT_RESULT
    )
    if(NOT CERT_RESULT EQUAL 0)
        message(WARNING "Failed to generate certificate. Run 'winapp cert generate' manually.")
    endif()
endif()

add_executable(cpp-app main.cpp)

# Link Windows Runtime libraries
target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib)

# Add Windows App SDK include directory
target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include)

Con esta configuración:

  • Cuando alguien clona el repositorio y ejecuta cmake -B build, winapp se descarga automáticamente si no se encuentra en PATH.
  • Los encabezados y el certificado de SDK de Aplicaciones para Windows se restauran automáticamente
  • Los comandos solo se ejecutan una vez durante la configuración (no en todas las compilaciones) porque comprueban si los archivos ya existen.
  • Si se produce un error en los comandos, CMake muestra una advertencia con instrucciones para ejecutarlos manualmente.
  • La winapp descargada se almacena en .winapp-tools/ (agréguelo a .gitignore si es necesario)

8. Paquete con MSIX

Una vez que esté listo para distribuir la aplicación, puede empaquetarla como MSIX mediante el mismo manifiesto. MSIX proporciona una instalación o desinstalación limpia, actualizaciones automáticas y una experiencia de instalación de confianza.

Preparar el directorio de paquetes

En primer lugar, compile la aplicación en modo de versión para obtener un rendimiento óptimo:

cmake --build build --config Release

A continuación, cree un directorio con solo los archivos necesarios para la distribución y copie el archivo ejecutable de versión:

mkdir dist
copy .\build\Release\cpp-app.exe .\dist\

Generación de un certificado de desarrollo

Los paquetes MSIX deben estar firmados. Para las pruebas locales, genere un certificado de desarrollo autofirmado:

winapp cert generate --if-exists skip

Sugerencia

El emisor del certificado debe coincidir con el Publisher en tu Package.appxmanifest. El cert generate comando lo lee automáticamente desde el manifiesto.

Firmar y empaquetar

Ahora puede empaquetar y firmar:

# package and sign the app with the generated certificate
winapp pack .\dist --cert .\devcert.pfx 

Sugerencia

El pack comando usa automáticamente Package.appxmanifest desde el directorio actual y lo copia en la carpeta de destino antes de empaquetar. El archivo generado .msix estará en el directorio actual.

Instalación del certificado

Para poder instalar el paquete MSIX, debe confiar en el certificado de desarrollo en el equipo. Ejecute este comando como administrador (solo tiene que hacerlo una vez por certificado):

winapp cert install .\devcert.pfx

Instalación y ejecución

Sugerencia

Si usó winapp run en el paso 5, es posible que el paquete ya esté registrado en el sistema. Use winapp unregister primero para quitar el registro de desarrollo y, a continuación, instale el paquete de versión.

El winapp pack comando genera el archivo MSIX en el directorio raíz del proyecto. Instale el paquete haciendo doble clic en el archivo generado .msix o mediante PowerShell:

Add-AppxPackage .\cpp-app_1.0.0.0_x64.msix

Sugerencia

El nombre de archivo MSIX incluye la versión y la arquitectura (por ejemplo, cpp-app_1.0.0.0_arm64.msix). Compruebe el directorio para ver el nombre de archivo exacto.

Ahora puede ejecutar la aplicación desde cualquier lugar del terminal escribiendo:

cpp-app

Debería ver la salida "Nombre de familia de paquete", confirmando que está instalada y en ejecución con la identidad.

Sugerencia

Si necesita volver a empaquetar la aplicación (por ejemplo, después de realizar cambios en el código), incremente Version en su Package.appxmanifest antes de volver a ejecutar winapp pack. Windows requiere un número de versión superior para actualizar un paquete instalado.

Tips

  1. Una vez que esté listo para su distribución, puede firmar su MSIX con un certificado de firma de código de una Autoridad de Certificación para que los usuarios no tengan que instalar un certificado autofirmado.
  2. El servicio Firma de confianza de Azure es una excelente manera de administrar los certificados de forma segura e integrar el inicio de sesión en la canalización de CI/CD.
  3. El Microsoft Store firmará el MSIX por usted, no es necesario firmar antes del envío.
  4. Es posible que tenga que crear varios paquetes MSIX, uno para cada arquitectura que admita (x64, Arm64). Configure CMake con las marcas de generador y arquitectura adecuadas.

Pasos siguientes