|
| 1 | +--- |
| 2 | +title: Using winapp CLI with C++ and CMake |
| 3 | +description: Learn how to use the winapp CLI with a C++ application to add package identity, access Windows APIs, and package as MSIX. |
| 4 | +ms.date: 02/20/2026 |
| 5 | +ms.topic: how-to |
| 6 | +--- |
| 7 | + |
| 8 | +# Using winapp CLI with C++ and CMake |
| 9 | + |
| 10 | +This guide demonstrates how to use the winapp CLI with a C++ application to debug with package identity and package your application as an MSIX. |
| 11 | + |
| 12 | +Package identity is a core concept in the Windows app model. It allows your application to access specific Windows APIs (like Notifications, Security, AI APIs, etc.), have a clean install/uninstall experience, and more. |
| 13 | + |
| 14 | +A standard executable (like one created with `cmake --build`) does not have package identity. This guide shows how to add it for debugging and then package it for distribution. |
| 15 | + |
| 16 | +## Prerequisites |
| 17 | + |
| 18 | +1. **Build Tools**: Use a compiler toolchain supported by CMake. This example uses Visual Studio. You can install the community edition with: |
| 19 | + |
| 20 | + ```powershell |
| 21 | + winget install --id Microsoft.VisualStudio.Community --source winget --override "--add Microsoft.VisualStudio.Workload.NativeDesktop --includeRecommended --passive --wait" |
| 22 | + ``` |
| 23 | +
|
| 24 | + Reboot after installation. |
| 25 | +
|
| 26 | +2. **CMake**: Install CMake: |
| 27 | +
|
| 28 | + ```powershell |
| 29 | + winget install Kitware.CMake --source winget |
| 30 | + ``` |
| 31 | +
|
| 32 | +3. **winapp CLI**: Install the `winapp` CLI via winget: |
| 33 | +
|
| 34 | + ```powershell |
| 35 | + winget install Microsoft.winappcli --source winget |
| 36 | + ``` |
| 37 | +
|
| 38 | +## 1. Create a new C++ app |
| 39 | +
|
| 40 | +Start by creating a simple C++ application. Create a new directory for your project: |
| 41 | +
|
| 42 | +```powershell |
| 43 | +mkdir cpp-app |
| 44 | +cd cpp-app |
| 45 | +``` |
| 46 | + |
| 47 | +Create a `main.cpp` file with a basic "Hello, world!" program: |
| 48 | + |
| 49 | +```cpp |
| 50 | +#include <iostream> |
| 51 | + |
| 52 | +int main() { |
| 53 | + std::cout << "Hello, world!" << std::endl; |
| 54 | + return 0; |
| 55 | +} |
| 56 | +``` |
| 57 | + |
| 58 | +Create a `CMakeLists.txt` file to configure the build: |
| 59 | + |
| 60 | +```cmake |
| 61 | +cmake_minimum_required(VERSION 3.20) |
| 62 | +project(cpp-app) |
| 63 | +
|
| 64 | +set(CMAKE_CXX_STANDARD 20) |
| 65 | +set(CMAKE_CXX_STANDARD_REQUIRED ON) |
| 66 | +
|
| 67 | +add_executable(cpp-app main.cpp) |
| 68 | +``` |
| 69 | + |
| 70 | +Build and run it to make sure everything is working: |
| 71 | + |
| 72 | +```powershell |
| 73 | +cmake -B build |
| 74 | +cmake --build build --config Debug |
| 75 | +.\build\Debug\cpp-app.exe |
| 76 | +``` |
| 77 | + |
| 78 | +## 2. Update code to check identity |
| 79 | + |
| 80 | +Update the app to check if it's running with package identity using the Windows Runtime C++ API. |
| 81 | + |
| 82 | +First, update your `CMakeLists.txt` to link against the Windows App Model library: |
| 83 | + |
| 84 | +```cmake |
| 85 | +cmake_minimum_required(VERSION 3.20) |
| 86 | +project(cpp-app) |
| 87 | +
|
| 88 | +set(CMAKE_CXX_STANDARD 20) |
| 89 | +set(CMAKE_CXX_STANDARD_REQUIRED ON) |
| 90 | +
|
| 91 | +add_executable(cpp-app main.cpp) |
| 92 | +
|
| 93 | +# Link Windows Runtime libraries |
| 94 | +target_link_libraries(cpp-app PRIVATE WindowsApp.lib OneCoreUap.lib) |
| 95 | +``` |
| 96 | + |
| 97 | +Next, replace the contents of `main.cpp`: |
| 98 | + |
| 99 | +```cpp |
| 100 | +#include <iostream> |
| 101 | +#include <windows.h> |
| 102 | +#include <appmodel.h> |
| 103 | + |
| 104 | +int main() { |
| 105 | + UINT32 length = 0; |
| 106 | + LONG result = GetCurrentPackageFamilyName(&length, nullptr); |
| 107 | + |
| 108 | + if (result == ERROR_INSUFFICIENT_BUFFER) { |
| 109 | + std::wstring familyName; |
| 110 | + familyName.resize(length); |
| 111 | + |
| 112 | + result = GetCurrentPackageFamilyName(&length, familyName.data()); |
| 113 | + |
| 114 | + if (result == ERROR_SUCCESS) { |
| 115 | + std::wcout << L"Package Family Name: " << familyName.c_str() << std::endl; |
| 116 | + } else { |
| 117 | + std::wcout << L"Error retrieving Package Family Name" << std::endl; |
| 118 | + } |
| 119 | + } else { |
| 120 | + std::cout << "Not packaged" << std::endl; |
| 121 | + } |
| 122 | + |
| 123 | + return 0; |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +## 3. Run without identity |
| 128 | + |
| 129 | +Rebuild and run the app: |
| 130 | + |
| 131 | +```powershell |
| 132 | +cmake --build build --config Debug |
| 133 | +.\build\Debug\cpp-app.exe |
| 134 | +``` |
| 135 | + |
| 136 | +You should see "Not packaged". This confirms that the standard executable is running without any package identity. |
| 137 | + |
| 138 | +## 4. Initialize project with winapp CLI |
| 139 | + |
| 140 | +The `winapp init` command sets up everything you need: app manifest, assets, and optionally Windows App SDK headers for C++ development. |
| 141 | + |
| 142 | +```powershell |
| 143 | +winapp init |
| 144 | +``` |
| 145 | + |
| 146 | +When prompted: |
| 147 | + |
| 148 | +- **Package name**: Press Enter to accept the default (cpp-app) |
| 149 | +- **Publisher name**: Press Enter to accept the default or enter your name |
| 150 | +- **Version**: Press Enter to accept 1.0.0.0 |
| 151 | +- **Entry point**: Press Enter to accept the default (cpp-app.exe) |
| 152 | +- **Setup SDKs**: Select "Stable SDKs" to download Windows App SDK and generate headers |
| 153 | + |
| 154 | +This command creates: |
| 155 | + |
| 156 | +- `appxmanifest.xml` and `Assets` folder for your app identity |
| 157 | +- A `.winapp` folder with Windows App SDK headers and libraries |
| 158 | +- A `winapp.yaml` configuration file for pinning SDK versions |
| 159 | + |
| 160 | +## 5. Debug with identity |
| 161 | + |
| 162 | +To test features that require identity without fully packaging the app, use `winapp create-debug-identity`: |
| 163 | + |
| 164 | +1. **Build the executable**: |
| 165 | + |
| 166 | + ```powershell |
| 167 | + cmake --build build --config Debug |
| 168 | + ``` |
| 169 | +
|
| 170 | +2. **Apply debug identity**: |
| 171 | +
|
| 172 | + ```powershell |
| 173 | + winapp create-debug-identity .\build\Debug\cpp-app.exe |
| 174 | + ``` |
| 175 | +
|
| 176 | +3. **Run the executable**: |
| 177 | +
|
| 178 | + ```powershell |
| 179 | + .\build\Debug\cpp-app.exe |
| 180 | + ``` |
| 181 | +
|
| 182 | +You should now see output similar to: |
| 183 | +
|
| 184 | +``` |
| 185 | +Package Family Name: cpp-app_12345abcde |
| 186 | +``` |
| 187 | +
|
| 188 | +### Automating debug identity (optional) |
| 189 | +
|
| 190 | +Add a post-build command to your `CMakeLists.txt` to automatically apply debug identity: |
| 191 | +
|
| 192 | +```cmake |
| 193 | +add_custom_command(TARGET cpp-app POST_BUILD |
| 194 | + COMMAND $<$<CONFIG:Debug>:winapp> |
| 195 | + $<$<CONFIG:Debug>:create-debug-identity> |
| 196 | + $<$<CONFIG:Debug>:$<TARGET_FILE:cpp-app>> |
| 197 | + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} |
| 198 | + COMMAND_EXPAND_LISTS |
| 199 | + COMMENT "Applying debug identity to executable..." |
| 200 | +) |
| 201 | +``` |
| 202 | + |
| 203 | +## 6. Using Windows App SDK (optional) |
| 204 | + |
| 205 | +If you selected to setup the SDKs during `winapp init`, you have access to Windows App SDK headers in the `.winapp/include` folder. |
| 206 | + |
| 207 | +Update your `CMakeLists.txt` to include the headers: |
| 208 | + |
| 209 | +```cmake |
| 210 | +# Add Windows App SDK include directory |
| 211 | +target_include_directories(cpp-app PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/.winapp/include) |
| 212 | +``` |
| 213 | + |
| 214 | +Update `main.cpp` to use the Windows App Runtime API: |
| 215 | + |
| 216 | +```cpp |
| 217 | +#include <iostream> |
| 218 | +#include <windows.h> |
| 219 | +#include <appmodel.h> |
| 220 | +#include <winrt/Microsoft.Windows.ApplicationModel.WindowsAppRuntime.h> |
| 221 | + |
| 222 | +int main() { |
| 223 | + winrt::init_apartment(); |
| 224 | + |
| 225 | + UINT32 length = 0; |
| 226 | + LONG result = GetCurrentPackageFamilyName(&length, nullptr); |
| 227 | + |
| 228 | + if (result == ERROR_INSUFFICIENT_BUFFER) { |
| 229 | + std::wstring familyName; |
| 230 | + familyName.resize(length); |
| 231 | + |
| 232 | + result = GetCurrentPackageFamilyName(&length, familyName.data()); |
| 233 | + |
| 234 | + if (result == ERROR_SUCCESS) { |
| 235 | + std::wcout << L"Package Family Name: " << familyName.c_str() << std::endl; |
| 236 | + |
| 237 | + auto runtimeVersion = winrt::Microsoft::Windows::ApplicationModel::WindowsAppRuntime::RuntimeInfo::AsString(); |
| 238 | + std::wcout << L"Windows App Runtime Version: " << runtimeVersion.c_str() << std::endl; |
| 239 | + } |
| 240 | + } else { |
| 241 | + std::cout << "Not packaged" << std::endl; |
| 242 | + } |
| 243 | + |
| 244 | + return 0; |
| 245 | +} |
| 246 | +``` |
| 247 | + |
| 248 | +## 7. Restore headers when needed |
| 249 | + |
| 250 | +The `.winapp` folder is automatically added to `.gitignore`. When others clone your project, they need to restore these files: |
| 251 | + |
| 252 | +```powershell |
| 253 | +winapp restore |
| 254 | +winapp cert generate --if-exists skip |
| 255 | +``` |
| 256 | + |
| 257 | +## 8. Package with MSIX |
| 258 | + |
| 259 | +Once you're ready to distribute, package as MSIX: |
| 260 | + |
| 261 | +1. **Build for release**: |
| 262 | + |
| 263 | + ```powershell |
| 264 | + cmake --build build --config Release |
| 265 | + ``` |
| 266 | +
|
| 267 | +2. **Prepare package directory**: |
| 268 | +
|
| 269 | + ```powershell |
| 270 | + mkdir dist |
| 271 | + copy .\build\Release\cpp-app.exe .\dist\ |
| 272 | + ``` |
| 273 | +
|
| 274 | +3. **Generate a development certificate**: |
| 275 | +
|
| 276 | + ```powershell |
| 277 | + winapp cert generate --if-exists skip |
| 278 | + ``` |
| 279 | +
|
| 280 | +4. **Package and sign**: |
| 281 | +
|
| 282 | + ```powershell |
| 283 | + winapp pack .\dist --cert .\devcert.pfx |
| 284 | + ``` |
| 285 | +
|
| 286 | +5. **Install the certificate** (run as administrator): |
| 287 | +
|
| 288 | + ```powershell |
| 289 | + winapp cert install .\devcert.pfx |
| 290 | + ``` |
| 291 | +
|
| 292 | +6. **Install and run**: |
| 293 | +
|
| 294 | + ```powershell |
| 295 | + Add-AppxPackage .\cpp-app.msix |
| 296 | + cpp-app |
| 297 | + ``` |
| 298 | +
|
| 299 | +> [!TIP] |
| 300 | +> - Sign your MSIX with a code signing certificate from a Certificate Authority for production distribution. |
| 301 | +> - The Microsoft Store signs the MSIX for you, no need to sign before submission. |
| 302 | +> - You may need separate MSIX packages for each architecture you support (x64, Arm64). |
| 303 | +
|
| 304 | +## Related topics |
| 305 | +
|
| 306 | +- [winapp CLI reference](../usage.md) |
| 307 | +- [winapp CLI overview](../index.md) |
| 308 | +- [Windows App SDK documentation](/windows/apps/windows-app-sdk/) |
0 commit comments