C++ と CMake での winapp CLI の使用

このガイドでは、 winapp CLI と C++ アプリケーションを使用してパッケージ ID を使用してデバッグし、アプリケーションを MSIX としてパッケージ化する方法について説明します。

パッケージ ID は、Windows app モデルの主要な概念です。 これにより、アプリケーションは特定のWindows API (通知、セキュリティ、AI API など) にアクセスでき、クリーンなインストール/アンインストール エクスペリエンスが提供されます。

標準の実行可能ファイル ( cmake --build で作成されたものなど) にはパッケージ ID がありません。 このガイドでは、デバッグ用に追加し、配布用にパッケージ化する方法を示します。

[前提条件]

  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

出力は "Hello, world!" にする必要があります。

2. コードを更新して ID を確認する

アプリがパッケージ ID で実行されているかどうかを確認するように更新します。 これは、後の手順で ID が正しく動作していることを確認するのに役立ちます。 Windows ランタイム C++ API を使用してパッケージ API にアクセスします。

まず、CMakeLists.txt の末尾に次の行を追加して、Windows アプリ モデル ライブラリにリンクします。

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

次に、 main.cpp の内容全体を次のコードに置き換えます。 このコードは、Windows ランタイム API を使用して現在のパッケージ ID の取得を試みます。 成功した場合は、パッケージ ファミリ名が出力されます。それ以外の場合は、"パッケージ化されていません" が出力されます。

#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. ID なしで実行する

ここで、通常どおりアプリをリビルドして実行します。

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

"パッケージ化されていません" という出力が表示されます。 これにより、標準の実行可能ファイルがパッケージ ID なしで実行されていることが確認されます。

4. winapp CLI を使用してProjectを初期化する

winapp init コマンドは、アプリ マニフェスト、アセット、および必要に応じて C++ 開発用のWindows アプリ SDKヘッダーなど、必要なものをすべて一度に設定します。

次のコマンドを実行し、プロンプトに従います。

winapp init .

プロンプトが表示されたら、次を実行します。

  • パッケージ名: Enter キーを押して既定値をそのまま使用します (cpp-app)
  • Publisher名: Enter キーを押して既定値をそのまま使用するか、名前を入力します
  • バージョン: Enter キーを押して 1.0.0.0 を受け入れる
  • エントリポイント: Enterキーを押してデフォルト (cpp-app.exe) を受け入れます。
  • Setup SDK: Windows アプリ SDKをダウンロードして C++ ヘッダーを生成するには、"Stable SDK" を選択します

このコマンドは次の操作を行います:

  • Package.appxmanifestの作成 - アプリの ID を定義するマニフェスト
  • Assets フォルダーの作成 - MSIX パッケージ化とストアの申請に必要なアイコン
  • Windows アプリ SDK ヘッダーとライブラリを含む .winapp フォルダーを作成する
  • SDK バージョンをピン留めするための winapp.yaml 構成ファイルを作成する

Package.appxmanifestを開いて、表示名、発行元、機能などのプロパティをさらにカスタマイズできます。

実行エイリアスの追加 (コンソール アプリの場合)

実行エイリアスを使用すると、ユーザーは任意のターミナル ( cpp-app など) から名前でアプリを実行できます。 また、開発中に winapp run --with-alias を有効にして、新しいウィンドウを開くのではなく、コンソール出力を現在のターミナルに保持します。

1 つを自動的に追加できます。

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. ID を使用したデバッグ

アプリを完全にパッケージ化せずに ID (通知など) を必要とする機能をテストするには、 winapp runを使用できます。 これにより、ルーズ レイアウト パッケージ (実際の MSIX インストールと同様) が登録され、1 つの手順でアプリが起動されます。 デバッグに証明書や署名は必要ありません。

  1. 実行可能ファイルをビルドします

    cmake --build build --config Debug
    
  2. ID を使用して実行する:

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

--with-alias フラグは、実行エイリアスを使用してアプリを起動し、コンソール出力が現在のターミナルに留まります。 これには、手順 4 で追加した uap5:ExecutionAlias が必要です。

ヒント

winapp run は、パッケージをシステムに登録します。 このため、手順 8 で後でインストールしようとすると、MSIX が "既にインストール済み" と表示されることがあります。 完了したら、 winapp unregister を使用して開発パッケージをクリーンアップします。

次のような出力が表示されます。

Package Family Name: cpp-app_12345abcde

これにより、アプリが有効なパッケージ ID で実行されていることを確認できます。

代替手段: スパース パッケージ ID

特にスパース パッケージの動作 (ファイルをコピーしない ID) が必要な場合は、代わりに create-debug-identity を使用できます。

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

ヒント

高度なデバッグ ワークフロー (デバッガーのアタッチ、IDE セットアップ、スタートアップ デバッグ) については、 デバッグ ガイドを参照してください。

6. Windows アプリ SDKの使用 (省略可能)

winapp init中に SDK をセットアップすることを選択した場合は、.winapp/include フォルダー内のWindows アプリ SDKヘッダーにアクセスできるようになりました。 これにより、通知、ウィンドウ化、デバイス上の AI などの最新のWindows API にアクセスできます。 配布にパッケージ ID が必要な場合は、手順 7 に進むことができます。

Windows アプリ ランタイム バージョンを出力する簡単な例を追加しましょう。

CMakeLists.txt の更新

Windows アプリ 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 を使用するには、main.cpp の内容全体を置き換えます。

#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 アプリ 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 アプリ SDKに必要なすべてのヘッダーが含まれています。

  • winrt/ - Windows ランタイム API にアクセスするための WinRT C++ プロジェクション ヘッダー
  • Microsoft.UI.*.h - 最新の UI コンポーネント用の WinUI 3 ヘッダー
  • MddBootstrap.h - Windows アプリ SDK のブートストラッピング
  • WindowsAppSDK-VersionInfo.h - バージョン情報
  • さらに多くのWindows アプリ SDKコンポーネント

Windows アプリ SDKの詳細については、Windows アプリ SDKドキュメントを参照してください。

7. 必要に応じヘッダーを復元する

.winapp フォルダーは.gitignoreによってwinapp initに自動的に追加されるため、ソース管理にはチェックインされません。 他のユーザーがプロジェクトを複製するときは、ビルドする前にこれらのファイルを復元する必要があります。

手動セットアップ

リポジトリを複製した後、次の 2 つのコマンドを実行します。

# 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 buildcmake --build build --config Debugを使用して通常どおりにビルドして実行できます。

CMake を使用した自動セットアップ

または、 CMakeLists.txtにセットアップ ロジックを追加して、これを自動化することもできます。 自動化、適切なリンク、最小限の C++20 標準を備えた完全な CMakeLists.txt を次に示します。

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実行すると、PATH に見つからない場合は winapp が自動的にダウンロードされます
  • Windows アプリ SDKヘッダーと証明書は自動的に復元されます
  • ファイルが既に存在するかどうかを確認するため、コマンドは構成中に 1 回だけ実行されます (すべてのビルドでは実行されません)。
  • コマンドが失敗した場合、手動で実行する手順を示す警告が 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

ヒント

証明書の発行者は、Package.appxmanifest 内の Publisher と一致する必要があります。 cert generate コマンドは、マニフェストからこれを自動的に読み取ります。

署名とパッケージ

これで、パッケージ化と署名を行うことができます。

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

ヒント

pack コマンドは、現在のディレクトリから Package.appxmanifest を自動的に使用し、パッケージ化する前にターゲット フォルダーにコピーします。 生成された .msix ファイルは現在のディレクトリに格納されます。

証明書をインストールする

MSIX パッケージをインストールする前に、コンピューター上の開発証明書を信頼する必要があります。 このコマンドを管理者として実行します (証明書ごとに 1 回だけ実行する必要があります)。

winapp cert install .\devcert.pfx

インストールと実行

ヒント

手順 5 で winapp run を使用した場合は、パッケージがシステムに既に登録されている可能性があります。 最初 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

"パッケージ ファミリ名" の出力が表示され、インストールされ、ID で実行されていることを確認します。

ヒント

(コード変更後など) アプリを再パッケージ化する必要がある場合は、Versionをもう一度実行する前に、Package.appxmanifestwinapp packをインクリメントします。 Windowsインストールされているパッケージを更新するには、より高いバージョン番号が必要です。

ヒント

  1. 配布の準備ができたら、証明機関のコード署名証明書を使用して MSIX に署名し、ユーザーが自己署名証明書をインストールする必要がないようにすることができます。
  2. Azure の信頼された署名 サービスは、証明書を安全に管理し、CI/CD パイプラインへの署名を統合するための優れた方法です。
  3. Microsoft Storeは MSIX に署名します。送信前に署名する必要はありません。
  4. サポートするアーキテクチャ (x64、Arm64) ごとに 1 つずつ、複数の MSIX パッケージを作成する必要がある場合があります。 適切なジェネレーターとアーキテクチャ フラグを使用して CMake を構成します。

次のステップ

  • winget を使用して配布: MSIX を Windows パッケージ マネージャー Community Repository に送信します
  • Microsoft Store に発行する: を使用してパッケージを送信する
  • CI/CD の設定: setup-WinAppCli GitHub Action を使用して、パイプライン内のパッケージングを自動化します
  • Explore Windows API: パッケージ ID を使用して、 これで、Notificationson-device AI、およびその他の identity 依存 API