使用 Winapp CLI 搭配 C++ 和 CMake

本指南示範如何在 C++ 應用程式中使用 winapp CLI 除錯套件識別碼,並將應用程式打包為 MSIX。

套件身份是 Windows app 模型中的核心概念。 它讓你的應用程式能access特定的Windows介面(例如通知、安全、AI 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:透過 winget 安裝 winapp CLI:

    winget install Microsoft.winappcli --source winget
    

1. 建立一個新的 C++ 應用程式

先從建立一個簡單的 C++ 應用程式開始。 為你的 project 建立一個新目錄:

mkdir cpp-app
cd cpp-app

建立一個包含基本「Hello, world!」程式的 main.cpp 檔案:

#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

2. 更新代碼以驗證身份

更新應用程式,以便使用 Windows Runtime C++ API 檢查其是否以套件身分執行。

首先,將你的 CMakeLists.txt 更新為連結至 Windows 應用程式模型函式庫:

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)

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

接著,替換以下 main.cpp內容:

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

int main() {
    UINT32 length = 0;
    LONG result = GetCurrentPackageFamilyName(&length, nullptr);

    if (result == ERROR_INSUFFICIENT_BUFFER) {
        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 {
        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)
  • Publisher name:按 Enter 接受預設或輸入你的名字
  • 版本:按下 "Enter" 以接受 1.0.0.0
  • 入口點:按 Enter 接受預設(cpp-app.exe)
  • Setup SDKs:選擇「Stable SDK」下載Windows App SDK並產生標頭

此指令會建立:

  • appxmanifest.xml 以及 Assets 用於應用程式識別的資料夾
  • 一個.winapp資料夾,包含Windows App SDK標頭與函式庫
  • 一個用於固定 SDK 版本的winapp.yaml設定檔

使用身份進行除錯

若要測試需要身份但未完全包裝應用程式的功能,請使用 winapp create-debug-identity

  1. 建立執行檔

    cmake --build build --config Debug
    
  2. 套用除錯身份

    winapp create-debug-identity .\build\Debug\cpp-app.exe
    
  3. 執行執行檔

    .\build\Debug\cpp-app.exe
    

你現在應該會看到類似的輸出:

Package Family Name: cpp-app_12345abcde

自動化偵錯識別(可選)

在你的 CMakeLists.txt 建置後新增一個指令,自動套用偵錯識別:

add_custom_command(TARGET cpp-app POST_BUILD
    COMMAND $<$<CONFIG:Debug>:winapp>
            $<$<CONFIG:Debug>:create-debug-identity>
            $<$<CONFIG:Debug>:$<TARGET_FILE:cpp-app>>
    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
    COMMAND_EXPAND_LISTS
    COMMENT "Applying debug identity to executable..."
)

6. 使用 Windows App SDK(可選)

如果你在 winapp init 時選擇設定 SDK,那麼你可以存取 .winapp/include 資料夾中的 Windows App SDK 標頭。

更新你的 CMakeLists.txt 的內容,加入標題:

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

更新 main.cpp 以使用 Windows 應用程式執行階段 API:

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

int main() {
    winrt::init_apartment();

    UINT32 length = 0;
    LONG result = GetCurrentPackageFamilyName(&length, nullptr);

    if (result == ERROR_INSUFFICIENT_BUFFER) {
        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;

            auto runtimeVersion = winrt::Microsoft::Windows::ApplicationModel::WindowsAppRuntime::RuntimeInfo::AsString();
            std::wcout << L"Windows App Runtime Version: " << runtimeVersion.c_str() << std::endl;
        }
    } else {
        std::cout << "Not packaged" << std::endl;
    }

    return 0;
}

7. 必要時還原標頭

資料夾 .winapp 會自動加入 .gitignore。 當其他人複製你的 project 時,他們需要還原以下這些檔案:

winapp restore
winapp cert generate --if-exists skip

8. MSIX 封裝

一旦準備好分發,請以 MSIX 格式打包:

  1. 為發佈建置

    cmake --build build --config Release
    
  2. 準備套件目錄

    mkdir dist
    copy .\build\Release\cpp-app.exe .\dist\
    
  3. 產生開發證書

    winapp cert generate --if-exists skip
    
  4. 包裝與標誌

    winapp pack .\dist --cert .\devcert.pfx
    
  5. 安裝憑證 (以管理員身份執行):

    winapp cert install .\devcert.pfx
    
  6. 安裝與執行

    Add-AppxPackage .\cpp-app.msix
    cpp-app
    

小提示

  • 請用來自憑證授權中心的程式碼簽署憑證來簽署你的 MSIX,以便生產分發。
  • Microsoft Store 會幫你簽署 MSIX,提交前不需要先簽。
  • 你可能需要為每個支援的架構(x64、Arm64)分別準備 MSIX 套件。