Использование winapp CLI с C++ и CMake

В этом руководстве показано, как использовать winapp CLI с приложением C++ для отладки с использованием идентификации пакетов и упаковки вашего приложения в формате MSIX.

Идентификатор пакета — это основная концепция модели приложений Windows. Это позволяет приложению получать доступ к определенным API-интерфейсам Windows (например, уведомлениям, безопасности, API ИИ и т. д.), иметь чистый интерфейс установки и удаления и многое другое.

Стандартный исполняемый файл (например, созданный с cmake --build) не имеет удостоверения пакета. В этом руководстве показано, как добавить его для отладки, а затем упаковать его для распространения.

Необходимые условия

  1. Средства сборки. Использование цепочки инструментов компилятора, поддерживаемой CMake. В этом примере используется Visual Studio. Вы можете установить выпуск сообщества с помощью (или обновить, если он уже установлен):

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

    Перезагрузите после установки.

  2. CMake: установите CMake (или обновите, если оно уже установлено):

    winget install Kitware.CMake --source winget
    
  3. winapp CLI: установите CLI через winget (или обновите winapp, если он уже установлен):

    winget install Microsoft.winappcli --source winget
    

1. Создание нового приложения C++

Начните с создания простого приложения C++. Создайте новую директорию для вашего проекта:

mkdir cpp-app
cd cpp-app

main.cpp Создайте файл с базовой программой Hello, world!":

#include <iostream>

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

CMakeLists.txt Создайте файл для настройки сборки:

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)

Выполните сборку и запустите ее, чтобы убедиться, что все работает:

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

Выходные данные должны быть "Hello, world!"

2. Обновление кода для проверки удостоверения

Мы обновим приложение, чтобы проверить, работает ли оно с идентификацией пакета. Это поможет нам удостовериться в правильной работе идентификации на следующих этапах. Мы будем использовать API среда выполнения Windows C++ для доступа к API пакетов.

Сначала добавьте следующую строку в конец CMakeLists.txt, чтобы связаться с библиотекой моделей приложение для Windows:

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

Затем замените всё содержимое main.cpp следующим кодом. Этот код пытается получить текущее удостоверение пакета с помощью API среда выполнения Windows. Если он успешно выполнен, он выводит имя семейства пакетов; в противном случае он выводит сообщение "Не упакованно".

#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. Запуск без идентификации

Теперь перестройте и запустите приложение как обычно:

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

Вы должны увидеть сообщение "Не упаковано". Это подтверждает, что стандартный исполняемый файл выполняется без идентификатора пакета.

4. Инициализируйте проект с помощью winapp CLI

Команда winapp init настраивает все необходимое в одном шаге: манифест приложения, ресурсы и заголовки Windows App SDK для разработки на C++ при необходимости.

Выполните следующую команду и следуйте подсказкам.

winapp init .

Когда появится запрос:

  • Имя пакета: нажмите Enter, чтобы принять значение по умолчанию (cpp-app)
  • Имя издателя: нажмите клавишу Enter, чтобы принять значение по умолчанию или введите своё имя.
  • Версия: нажмите клавишу ВВОД, чтобы принять 1.0.0.0
  • Точка входа: нажмите клавишу ВВОД, чтобы принять значение по умолчанию (cpp-app.exe)
  • Настройка SDK: выберите "Стабильные SDK", чтобы скачать Windows App SDK и сгенерировать заголовки C++

Эта команда выполнит следующее действие:

  • Создайте Package.appxmanifest — манифест, определяющий идентичность вашего приложения.
  • Создание Assets папки — значки, необходимые для упаковки MSIX и отправки в Магазин
  • Создание папки .winapp с заголовками и библиотеками Windows App SDK
  • Создайте файл конфигурации winapp.yaml для закрепления версий пакета SDK

Вы можете открыть Package.appxmanifest, чтобы дополнительно настроить такие свойства, как отображаемое имя, издатель и возможности.

Добавление псевдонима запуска (для консольных приложений)

Псевдоним выполнения позволяет пользователям запускать приложение по имени из любого терминала (например cpp-app). Во время разработки он также позволяет включить функцию winapp run --with-alias, что позволяет сохранять вывод консоли в текущем терминале вместо открытия нового окна.

Вы можете добавить его автоматически:

winapp manifest add-alias

Или вручную: откройте Package.appxmanifest и добавьте uap5 пространство имен в <Package> тег, если он отсутствует, а затем добавьте расширение внутри <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. Отладка с помощью идентификации

Для тестирования функций, требующих удостоверения (например, уведомлений) без полной упаковки приложения, можно использовать winapp run. Он регистрирует свободный пакет компоновки (как и реальную установку MSIX) и запускает приложение в один шаг. Для отладки не требуется сертификат или подпись.

  1. Создайте исполняемый файл:

    cmake --build build --config Debug
    
  2. Запустите с идентификацией:

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

Флаг --with-alias запускает приложение с помощью псевдонима выполнения, чтобы выходные данные консоли оставались в текущем терминале. Для этого требуется uap5:ExecutionAlias, который мы добавили на шаге 4.

Подсказка

winapp run также регистрирует пакет в системе. Поэтому MSIX может отображаться как "уже установлен" при попытке установить его позже на шаге 8. Используйте winapp unregister для очистки пакетов разработки по завершении.

Теперь вы увидите выходные данные, аналогичные следующим:

Package Family Name: cpp-app_12345abcde

Это подтверждает, что ваше приложение работает с действительным удостоверением пакета!

Альтернатива: упрощённая идентификация пакета

Если требуется разреженное поведение пакета (идентификация без копирования файлов), вместо этого можно использовать create-debug-identity:.

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

Подсказка

Дополнительные рабочие процессы отладки (присоединение отладчиков, настройка интегрированной среды разработки, отладка запуска) см. в руководстве по отладке.

6. Использование Windows App SDK (необязательно)

Если вы выбрали настройку пакетов SDK во время winapp init, теперь у вас есть доступ к заголовкам Windows App SDK в папке .winapp/include. Благодаря этому вы получаете доступ к современным api-интерфейсам Windows, таким как уведомления, окна, ИИ на устройстве и многое другое. Если вам просто нужен идентификатор пакета для распространения, можно перейти к шагу 7.

Добавим простой пример, который выводит версию приложение для Windows Runtime.

Обновите CMakeLists.txt

Добавьте следующую строку в конец CMakeLists.txt, чтобы включить заголовки Windows App SDK:

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

Обновите main.cpp

Замените все содержимое main.cpp, чтобы использовать приложение для Windows Runtime API:

#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;
}

Сборка и запуск

Перестройте приложение, используя заголовки Windows App SDK.

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

Теперь вы увидите такие выходные данные:

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

Каталог .winapp/include содержит все необходимые заголовки для Windows App SDK, включая:

  • winrt/ — заголовки проекции WinRT C++ для доступа к API среда выполнения Windows
  • Microsoft.UI.*.h — заголовки WinUI 3 для современных компонентов пользовательского интерфейса
  • MddBootstrap.h — инициализация Windows App SDK
  • WindowsAppSDK-VersionInfo.h — Сведения о версии
  • И многие другие Windows App SDK компоненты

Дополнительные сведения об использовании Windows App SDK см. в документации Windows App SDK.

7. Восстановление заголовков при необходимости

Папка .winapp автоматически добавляется в .gitignore через winapp init, поэтому она не будет добавлена в систему управления версиями. Когда другие клонируют проект, перед сборкой им потребуется восстановить эти файлы.

Настройка вручную

Выполните две команды после клонирования репозитория:

# 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

Затем вы можете создать и запустить как обычно с cmake -B build и cmake --build build --config Debug.

Автоматическая настройка с помощью CMake

Кроме того, это можно автоматизировать, добавив логику настройки в вашу CMakeLists.txt. Вот полный CMakeLists.txt с автоматизацией, правильной связью и минимальными элементами стандарта 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)

С помощью этой установки выполните указанные ниже действия.

  • Когда кто-то клонирует репозиторий и запускает cmake -B build, winapp автоматически скачивается, если он не найден в PATH
  • Заголовки и сертификаты Windows App SDK автоматически восстанавливаются
  • Команды выполняются только один раз во время настройки (не во всех сборках), так как они проверяют, уже существуют ли файлы.
  • Если команды завершаются ошибкой, CMake отображает предупреждение с инструкциями по их выполнению вручную.
  • Загруженный winapp хранится в .winapp-tools/ (при необходимости добавьте его в .gitignore)

8. Пакет с MSIX

Когда вы будете готовы к распространению приложения, его можно упаковать как MSIX с помощью того же манифеста. MSIX обеспечивает чистую установку и удаление, автоматическое обновление и доверенный интерфейс установки.

Подготовка каталога пакетов

Сначала создайте приложение в режиме выпуска для оптимальной производительности:

cmake --build build --config Release

Затем создайте каталог, содержащий только файлы, необходимые для распространения, и скопируйте ваш исполняемый файл релиза.

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

Создание сертификата разработки

Пакеты MSIX должны быть подписаны. Для локального тестирования создайте самозаверяющий сертификат разработки:

winapp cert generate --if-exists skip

Подсказка

Издатель сертификата должен соответствовать Publisher в Package.appxmanifest. Команда cert generate автоматически считывает это из манифеста.

Подписание и упаковка

Теперь вы можете упаковывать и подписывать:

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

Подсказка

Команда pack автоматически использует Package.appxmanifest из текущего каталога и копирует его в целевую папку перед упаковкой. Созданный .msix файл будет находиться в текущем каталоге.

Установка сертификата

Прежде чем установить пакет MSIX, необходимо доверять сертификату разработки на компьютере. Выполните эту команду от имени администратора (для каждого сертификата необходимо выполнить только один раз):

winapp cert install .\devcert.pfx

Установка и запуск

Подсказка

Если вы использовали winapp run на шаге 5, пакет уже может быть зарегистрирован в вашей системе. winapp unregister Сначала следует удалить регистрацию разработки, а затем установить финальный пакет.

Команда winapp pack создает MSIX-файл в корневом каталоге проекта. Установите пакет, дважды щелкнув созданный .msix файл или используя PowerShell:

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

Подсказка

Имя файла MSIX включает версию и архитектуру (например, cpp-app_1.0.0.0_arm64.msix). Проверьте каталог для точного имени файла.

Теперь вы можете запустить приложение из любого места в терминале, введя следующее:

cpp-app

Вы должны увидеть вывод "Package Family Name", подтверждающий его установку и запуск с удостоверением.

Подсказка

Если вам нужно перепаковать приложение (например, после изменения кода), увеличьте Version в вашем Package.appxmanifest перед выполнением winapp pack еще раз. Windows требуется более высокий номер версии для обновления установленного пакета.

Tips

  1. После подготовки к распространению вы можете подписать MSIX с помощью сертификата подписи кода из центра сертификации, чтобы пользователи не должны устанавливать самозаверяющий сертификат.
  2. Служба Azure Trusted Signing — отличный способ безопасного управления сертификатами и интеграции входа в конвейер CI/CD.
  3. Microsoft Store подпишет MSIX за вас, так что нет необходимости подписывать его перед отправкой.
  4. Может потребоваться создать несколько пакетов MSIX, по одному для каждой поддерживаемой архитектуры (x64, Arm64). Настройте CMake с соответствующими флагами генератора и архитектуры.

Дальнейшие шаги

  • Распространите с помощью winget: отправьте MSIX в репозиторий сообщества Windows диспетчер пакетов
  • Опубликовать в Microsoft Store: используйте winapp store для отправки пакета.
  • Set up CI/CD. Используйте действие setup-WinAppCli GitHub для автоматизации упаковки в конвейере.
  • Ознакомьтесь с Windows API: Теперь, с удостоверением пакета, вы можете использовать Уведомления, встроенный ИИ, и другие API-интерфейсы, зависящие от удостоверения