diff --git a/Samples/CMake/CMakeLists.txt b/Samples/CMake/CMakeLists.txt new file mode 100644 index 000000000..766b5dc62 --- /dev/null +++ b/Samples/CMake/CMakeLists.txt @@ -0,0 +1,59 @@ +#---------------------------------------------------------------------------------------------------------------------- +# CMakeBaselineTests - WinAppSDK Deployment Matrix Baseline Tests (CMake + NuGetCMakePackage) +#---------------------------------------------------------------------------------------------------------------------- +cmake_minimum_required(VERSION 3.31) + +cmake_policy(SET CMP0141 NEW) +cmake_policy(SET CMP0117 NEW) + +if(CMAKE_GENERATOR MATCHES "^Visual Studio") + file(WRITE "${CMAKE_BINARY_DIR}/Directory.Build.rsp" "-m -graphBuild:true -p:UseMultiToolTask=true -nodeReuse:false -terminalLogger:auto") + file(WRITE "${CMAKE_BINARY_DIR}/Directory.Build.props" "") + file(WRITE "${CMAKE_BINARY_DIR}/Directory.Build.targets" "") +elseif(CMAKE_GENERATOR MATCHES "^Ninja") + if(NOT (DEFINED ENV{Platform})) + message(FATAL_ERROR "When using the Ninja generator, you must build from a platform-specific Developer Command Prompt.") + endif() +endif() + +project(CMakeBaselineTests LANGUAGES CXX) + +include(FetchContent) + +FetchContent_Declare( + NuGetCMakePackage + GIT_REPOSITORY https://github.com/mschofie/NuGetCMakePackage + GIT_TAG user/ssparach/prototype +) + +FetchContent_MakeAvailable(NuGetCMakePackage) + +add_nuget_packages( + PACKAGES + Microsoft.Windows.CppWinRT 2.0.251203.1 + Microsoft.WindowsAppSDK.Foundation 2.0.15-preview + Microsoft.WindowsAppSDK.InteractiveExperiences 2.0.6-preview + Microsoft.WindowsAppSDK.Runtime 2.0.0-preview1 +) + +find_package(Microsoft.WindowsAppSDK.Foundation CONFIG REQUIRED) + +# Common configuration +include(${CMAKE_SOURCE_DIR}/Configuration.cmake) +link_libraries(Configuration) + +# Macro to copy runtime DLLs to the output directory +macro(post_build_runtime_dll_copy target_name) + if(WIN32) + add_custom_command(TARGET ${target_name} POST_BUILD + COMMAND "${CMAKE_COMMAND};-E;$>,copy;$;$,true>" + COMMAND_EXPAND_LISTS + ) + endif() +endmacro() + +# Subdirectories +add_subdirectory(UnpackagedSelfContained) +add_subdirectory(UnpackagedFrameworkDependent) +add_subdirectory(PackagedSelfContained) +add_subdirectory(PackagedFrameworkDependent) diff --git a/Samples/CMake/CMakePresets.json b/Samples/CMake/CMakePresets.json new file mode 100644 index 000000000..71b79d065 --- /dev/null +++ b/Samples/CMake/CMakePresets.json @@ -0,0 +1,64 @@ +{ + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 31, + "patch": 0 + }, + "configurePresets": [ + { + "name": "vs2022-x64", + "displayName": "Visual Studio 2022 x64", + "generator": "Visual Studio 17 2022", + "architecture": "x64", + "binaryDir": "${sourceDir}/build/vs2022-x64" + }, + { + "name": "vs2022-arm64", + "displayName": "Visual Studio 2022 ARM64", + "generator": "Visual Studio 17 2022", + "architecture": "ARM64", + "binaryDir": "${sourceDir}/build/vs2022-arm64" + }, + { + "name": "ninja-x64", + "displayName": "Ninja Multi-Config x64", + "generator": "Ninja Multi-Config", + "binaryDir": "D:/b/nx64", + "cacheVariables": { + "CMAKE_OBJECT_PATH_MAX": "1000" + } + }, + { + "name": "ninja-arm64", + "displayName": "Ninja Multi-Config ARM64", + "generator": "Ninja Multi-Config", + "binaryDir": "D:/b/na64", + "cacheVariables": { + "CMAKE_OBJECT_PATH_MAX": "1000" + } + } + ], + "buildPresets": [ + { + "name": "vs2022-x64-debug", + "configurePreset": "vs2022-x64", + "configuration": "Debug" + }, + { + "name": "vs2022-x64-release", + "configurePreset": "vs2022-x64", + "configuration": "Release" + }, + { + "name": "ninja-x64-debug", + "configurePreset": "ninja-x64", + "configuration": "Debug" + }, + { + "name": "ninja-x64-release", + "configurePreset": "ninja-x64", + "configuration": "Release" + } + ] +} diff --git a/Samples/CMake/Configuration.cmake b/Samples/CMake/Configuration.cmake new file mode 100644 index 000000000..f5fdd38fe --- /dev/null +++ b/Samples/CMake/Configuration.cmake @@ -0,0 +1,31 @@ +#---------------------------------------------------------------------------------------------------------------------- +# Common configuration for all baseline test apps +#---------------------------------------------------------------------------------------------------------------------- +add_library(Configuration INTERFACE) + +target_compile_definitions(Configuration + INTERFACE + UNICODE + _UNICODE + NOMINMAX + WIN32_LEAN_AND_MEAN +) + +target_compile_features(Configuration + INTERFACE + cxx_std_20 +) + +target_compile_options(Configuration + INTERFACE + $<$:/EHsc> + $<$:/W3> + $<$:/Zc:__cplusplus> + $<$:/Zc:preprocessor> +) + +target_link_options(Configuration + INTERFACE + $<$:$<$>:/LTCG>> + $<$:$<$>:/INCREMENTAL:NO>> +) diff --git a/Samples/CMake/Launch-PackagedApp.ps1 b/Samples/CMake/Launch-PackagedApp.ps1 new file mode 100644 index 000000000..db6cba37d --- /dev/null +++ b/Samples/CMake/Launch-PackagedApp.ps1 @@ -0,0 +1,66 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +<# +.SYNOPSIS + Registers and launches a packaged MSIX app with package identity. +.PARAMETER AppxManifestPath + Path to the AppxManifest.xml in the build output directory. +.PARAMETER PackageNameFilter + Wildcard filter to find the registered package (e.g., *CMake-PackagedSelfContained*). +.EXAMPLE + .\Launch-PackagedApp.ps1 -AppxManifestPath .\build\vs2022-x64\PackagedSelfContained\Debug\AppxManifest.xml -PackageNameFilter *CMake-PackagedSelfContained* +#> + +param( + [Parameter(Mandatory)] + [string]$AppxManifestPath, + + [Parameter(Mandatory)] + [string]$PackageNameFilter +) + +Set-StrictMode -Version 2.0 +$ErrorActionPreference = 'Stop' + +# Step 1: Register +Write-Host "Registering MSIX layout: $AppxManifestPath" +Add-AppxPackage -Register $AppxManifestPath + +# Step 2: Activate via COM IApplicationActivationManager (provides package identity) +Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +[ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public interface IAppActivation { + IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] UInt32 options, [Out] out UInt32 processId); +} + +[ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] +public class AppActivation : IAppActivation { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] UInt32 options, [Out] out UInt32 processId); +} +"@ -ErrorAction SilentlyContinue + +$pkg = Get-AppxPackage | Where-Object { $_.Name -like $PackageNameFilter } +if (-not $pkg) { + Write-Error "Package not found matching '$PackageNameFilter'" + exit 1 +} + +$aumid = "$($pkg.PackageFamilyName)!App" +Write-Host "Launching: $aumid" + +$aam = New-Object AppActivation +$processId = [uint32]0 +$aam.ActivateApplication($aumid, $null, 0, [ref]$processId) | Out-Null + +Write-Host "App launched with PID: $processId" +Write-Host "" +Write-Host "To unregister when done:" +Write-Host " Get-AppxPackage $PackageNameFilter | Remove-AppxPackage" + +exit 0 diff --git a/Samples/CMake/PackagedFrameworkDependent/AppxManifest.xml b/Samples/CMake/PackagedFrameworkDependent/AppxManifest.xml new file mode 100644 index 000000000..3b8d39509 --- /dev/null +++ b/Samples/CMake/PackagedFrameworkDependent/AppxManifest.xml @@ -0,0 +1,31 @@ + + + + + + PackagedFrameworkDependent (CMake) + Microsoft + Logo.png + + + + + + + + + + + + + + + + + diff --git a/Samples/CMake/PackagedFrameworkDependent/CMakeLists.txt b/Samples/CMake/PackagedFrameworkDependent/CMakeLists.txt new file mode 100644 index 000000000..974502272 --- /dev/null +++ b/Samples/CMake/PackagedFrameworkDependent/CMakeLists.txt @@ -0,0 +1,33 @@ +#---------------------------------------------------------------------------------------------------------------------- +# PackagedFrameworkDependent - Framework-dependent deployment with MSIX packaging +# +# After building, register the MSIX layout and launch via AUMID: +# Add-AppxPackage -Register \PackagedFrameworkDependent\AppxManifest.xml +# Start-Process "shell:AppsFolder\!App" +#---------------------------------------------------------------------------------------------------------------------- +project(PackagedFrameworkDependent LANGUAGES CXX) + +add_executable(PackagedFrameworkDependent WIN32 + main.cpp +) + +# Framework-dependent: use dynamic CRT +set_target_properties(PackagedFrameworkDependent PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL" +) + +# Use Foundation_Framework but without bootstrapper (MSIX provides framework package via package graph) +target_link_libraries(PackagedFrameworkDependent + PRIVATE + Microsoft.WindowsAppSDK.Foundation_Framework +) + +post_build_runtime_dll_copy(PackagedFrameworkDependent) + +# Copy AppxManifest.xml and Logo to the output directory for MSIX registration +add_custom_command(TARGET PackagedFrameworkDependent POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/AppxManifest.xml + ${CMAKE_CURRENT_SOURCE_DIR}/Logo.png + $ +) diff --git a/Samples/CMake/PackagedFrameworkDependent/Logo.png b/Samples/CMake/PackagedFrameworkDependent/Logo.png new file mode 100644 index 000000000..5e12fa828 Binary files /dev/null and b/Samples/CMake/PackagedFrameworkDependent/Logo.png differ diff --git a/Samples/CMake/PackagedFrameworkDependent/README.md b/Samples/CMake/PackagedFrameworkDependent/README.md new file mode 100644 index 000000000..812604f5e --- /dev/null +++ b/Samples/CMake/PackagedFrameworkDependent/README.md @@ -0,0 +1,65 @@ +# PackagedFrameworkDependent + +A native C++ WinAppSDK app that is **packaged** (MSIX) and **framework-dependent**, built with CMake and NuGetCMakePackage. + +## What this app demonstrates + +- MSIX-packaged deployment with package identity +- Framework-dependent WinAppSDK deployment — the WinAppSDK Framework package is resolved via the MSIX package graph +- Standard dynamic CRT linking +- WinAppSDK EnvironmentManager API via C++/WinRT projections +- Manual AppxManifest with explicit `PackageDependency` on the WinAppSDK framework + +## Configuration + +- CMake target: `Microsoft.WindowsAppSDK.Foundation_Framework` +- CRT: `MultiThreadedDLL` / `MultiThreadedDebugDLL` (standard dynamic CRT) +- MSIX packaging via manual `AppxManifest.xml` with `PackageDependency` on `Microsoft.WindowsAppRuntime.2.0-preview1` + +## Build + +From the `CMakeBaselineTests/` directory: + +```powershell +# Configure (one-time) +cmake --preset vs2022-x64 + +# Build +cmake --build --preset vs2022-x64-debug +``` + +Or for Release: + +```powershell +cmake --build --preset vs2022-x64-release +``` + +## Launch + +This app requires MSIX registration to get package identity and resolve the framework package: + +```powershell +# Step 1: Register the MSIX package layout +Add-AppxPackage -Register ".\build\vs2022-x64\PackagedFrameworkDependent\Debug\AppxManifest.xml" + +# Step 2: Launch via shell activation (provides package identity + framework resolution) +$pfn = (Get-AppxPackage *CMake-PackagedFrameworkDependent*).PackageFamilyName +Start-Process "shell:AppsFolder\${pfn}!App" + +# Step 3: Cleanup when done +Get-AppxPackage *CMake-PackagedFrameworkDependent* | Remove-AppxPackage +``` + +**Expected result:** A message box showing "Packaged Framework-Dependent App (CMake)" with the full package identity string, EnvironmentManager status, and the `PROCESSOR_ARCHITECTURE` value. + +> **Note:** The app must be launched through the MSIX activation mechanism to have package identity. In Visual Studio, use F5 with the packaging project as startup. + +## Prerequisites + +- **WinAppSDK runtime** (`Microsoft.WindowsAppRuntime.2.0-preview1`) must be installed on the machine + +## Notes + +- The `AppxManifest.xml` declares a `PackageDependency` on `Microsoft.WindowsAppRuntime.2.0-preview1` which causes Windows to resolve and load the framework package when the MSIX is activated. +- Unlike self-contained, the app has a smaller footprint since WinAppSDK binaries come from the shared framework package. +- No bootstrapper is used — the MSIX package graph handles framework resolution. diff --git a/Samples/CMake/PackagedFrameworkDependent/main.cpp b/Samples/CMake/PackagedFrameworkDependent/main.cpp new file mode 100644 index 000000000..c4fe532f9 --- /dev/null +++ b/Samples/CMake/PackagedFrameworkDependent/main.cpp @@ -0,0 +1,116 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include + +#include +#include +#include + +int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR, _In_ int) +{ + // Write results to a log file in the temp directory + wchar_t tempPath[MAX_PATH]{}; + GetTempPathW(MAX_PATH, tempPath); + std::wstring logPath = std::wstring(tempPath) + L"CMake_PackagedFW.log"; + std::wofstream log(logPath, std::ios::trunc); + + try + { + winrt::init_apartment(); + + std::wostringstream message; + message << L"Packaged Framework-Dependent App (CMake)\n\n"; + + // Check package identity + UINT32 nameLen = 0; + auto rc = GetCurrentPackageFullName(&nameLen, nullptr); + if (rc == APPMODEL_ERROR_NO_PACKAGE) + { + message << L"Package Identity: NONE\n"; + log << L"PackageIdentity=NONE" << std::endl; + } + else + { + std::wstring pkgName(nameLen, L'\0'); + GetCurrentPackageFullName(&nameLen, pkgName.data()); + // Remove trailing null that GetCurrentPackageFullName includes in the length + if (!pkgName.empty() && pkgName.back() == L'\0') + { + pkgName.pop_back(); + } + message << L"Package Identity: " << pkgName << L"\n"; + log << L"PackageIdentity=" << pkgName << std::endl; + } + + // Use WinAppSDK API - try EnvironmentManager, fall back to Win32 + if (winrt::Microsoft::Windows::System::EnvironmentManager::IsSupported()) + { + auto envManager = winrt::Microsoft::Windows::System::EnvironmentManager::GetForProcess(); + auto value = envManager.GetEnvironmentVariable(L"PROCESSOR_ARCHITECTURE"); + message << L"EnvironmentManager: Supported\n"; + message << L"PROCESSOR_ARCHITECTURE: " << std::wstring(value) << L"\n"; + log << L"EnvironmentManager=Supported" << std::endl; + log << L"PROCESSOR_ARCHITECTURE=" << std::wstring(value) << std::endl; + } + else + { + message << L"EnvironmentManager: Not supported\n"; + log << L"EnvironmentManager=NotSupported" << std::endl; + } + + // Always show something to prove WinAppSDK loaded + wchar_t arch[64]{}; + GetEnvironmentVariableW(L"PROCESSOR_ARCHITECTURE", arch, 64); + message << L"Win32 PROCESSOR_ARCHITECTURE: " << arch << L"\n"; + + // Use WinAppSDK PowerManager to query battery and power state + namespace Power = winrt::Microsoft::Windows::System::Power; + + message << L"\n"; + auto batteryStatus = Power::PowerManager::BatteryStatus(); + message << L"PowerManager.BatteryStatus: "; + switch (batteryStatus) + { + case Power::BatteryStatus::NotPresent: message << L"NotPresent"; break; + case Power::BatteryStatus::Discharging: message << L"Discharging"; break; + case Power::BatteryStatus::Idle: message << L"Idle"; break; + case Power::BatteryStatus::Charging: message << L"Charging"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + auto powerSupply = Power::PowerManager::PowerSupplyStatus(); + message << L"PowerManager.PowerSupplyStatus: "; + switch (powerSupply) + { + case Power::PowerSupplyStatus::NotPresent: message << L"NotPresent"; break; + case Power::PowerSupplyStatus::Inadequate: message << L"Inadequate"; break; + case Power::PowerSupplyStatus::Adequate: message << L"Adequate"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + message << L"PowerManager.RemainingChargePercent: " + << Power::PowerManager::RemainingChargePercent() << L"%\n"; + + log << L"Result=SUCCESS" << std::endl; + log.close(); + MessageBoxW(nullptr, message.str().c_str(), L"WinAppSDK CMake Test", MB_OK | MB_ICONINFORMATION); + } + catch (winrt::hresult_error const& ex) + { + log << L"Error=0x" << std::hex << ex.code() << std::endl; + log.close(); + std::wostringstream err; + err << L"WinRT error 0x" << std::hex << ex.code() << L"\n" << ex.message().c_str(); + MessageBoxW(nullptr, err.str().c_str(), L"WinAppSDK Error", MB_OK | MB_ICONERROR); + return 1; + } + + return 0; +} diff --git a/Samples/CMake/PackagedSelfContained/AppxManifest.xml b/Samples/CMake/PackagedSelfContained/AppxManifest.xml new file mode 100644 index 000000000..9564696d9 --- /dev/null +++ b/Samples/CMake/PackagedSelfContained/AppxManifest.xml @@ -0,0 +1,29 @@ + + + + + + PackagedSelfContained (CMake) + Microsoft + Logo.png + + + + + + + + + + + + + + + + diff --git a/Samples/CMake/PackagedSelfContained/CMakeLists.txt b/Samples/CMake/PackagedSelfContained/CMakeLists.txt new file mode 100644 index 000000000..a40f1feb0 --- /dev/null +++ b/Samples/CMake/PackagedSelfContained/CMakeLists.txt @@ -0,0 +1,33 @@ +#---------------------------------------------------------------------------------------------------------------------- +# PackagedSelfContained - Self-contained deployment with MSIX packaging +# +# After building, register the MSIX layout and launch via AUMID: +# Add-AppxPackage -Register \PackagedSelfContained\AppxManifest.xml +# Start-Process "shell:AppsFolder\!App" +#---------------------------------------------------------------------------------------------------------------------- +project(PackagedSelfContained LANGUAGES CXX) + +add_executable(PackagedSelfContained WIN32 + main.cpp +) + +# Self-contained: use static VC runtime (Hybrid CRT) +set_target_properties(PackagedSelfContained PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) + +target_link_libraries(PackagedSelfContained + PRIVATE + Microsoft.WindowsAppSDK.Foundation_SelfContained + Microsoft.WindowsAppSDK.InteractiveExperiences_SelfContained +) + +post_build_runtime_dll_copy(PackagedSelfContained) + +# Copy AppxManifest.xml and Logo to the output directory for MSIX registration +add_custom_command(TARGET PackagedSelfContained POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${CMAKE_CURRENT_SOURCE_DIR}/AppxManifest.xml + ${CMAKE_CURRENT_SOURCE_DIR}/Logo.png + $ +) diff --git a/Samples/CMake/PackagedSelfContained/Logo.png b/Samples/CMake/PackagedSelfContained/Logo.png new file mode 100644 index 000000000..5e12fa828 Binary files /dev/null and b/Samples/CMake/PackagedSelfContained/Logo.png differ diff --git a/Samples/CMake/PackagedSelfContained/README.md b/Samples/CMake/PackagedSelfContained/README.md new file mode 100644 index 000000000..d18712e5a --- /dev/null +++ b/Samples/CMake/PackagedSelfContained/README.md @@ -0,0 +1,61 @@ +# PackagedSelfContained + +A native C++ WinAppSDK app that is **packaged** (MSIX) and **self-contained**, built with CMake and NuGetCMakePackage. + +## What this app demonstrates + +- MSIX-packaged deployment with package identity +- Self-contained WinAppSDK deployment — all WinAppSDK DLLs are included in the MSIX layout +- Hybrid CRT (static VC runtime + dynamic UCRT) +- WinAppSDK EnvironmentManager API via C++/WinRT projections +- Manual AppxManifest for CMake-built packaged apps + +## Configuration + +- CMake target: `Microsoft.WindowsAppSDK.Foundation_SelfContained` +- CRT: `MultiThreaded` / `MultiThreadedDebug` (Hybrid CRT) +- MSIX packaging via manual `AppxManifest.xml` copied to build output + +## Build + +From the `CMakeBaselineTests/` directory: + +```powershell +# Configure (one-time) +cmake --preset vs2022-x64 + +# Build +cmake --build --preset vs2022-x64-debug +``` + +Or for Release: + +```powershell +cmake --build --preset vs2022-x64-release +``` + +## Launch + +This app requires MSIX registration to get package identity: + +```powershell +# Step 1: Register the MSIX package layout +Add-AppxPackage -Register ".\build\vs2022-x64\PackagedSelfContained\Debug\AppxManifest.xml" + +# Step 2: Launch via shell activation (provides package identity) +$pfn = (Get-AppxPackage *CMake-PackagedSelfContained*).PackageFamilyName +Start-Process "shell:AppsFolder\${pfn}!App" + +# Step 3: Cleanup when done +Get-AppxPackage *CMake-PackagedSelfContained* | Remove-AppxPackage +``` + +**Expected result:** A message box showing "Packaged Self-Contained App (CMake)" with the full package identity string, EnvironmentManager status, and the `PROCESSOR_ARCHITECTURE` value. + +> **Note:** The app must be launched through the MSIX activation mechanism to have package identity. In Visual Studio, use F5 with the packaging project as startup. + +## Notes + +- Since this is self-contained, no WinAppSDK runtime installer is needed on the target machine. +- The `AppxManifest.xml` and `Logo.png` are copied to the build output by a post-build command. +- The app does not declare a framework package dependency in the manifest since all dependencies are self-contained. diff --git a/Samples/CMake/PackagedSelfContained/main.cpp b/Samples/CMake/PackagedSelfContained/main.cpp new file mode 100644 index 000000000..20d3a7abd --- /dev/null +++ b/Samples/CMake/PackagedSelfContained/main.cpp @@ -0,0 +1,120 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include +#include +#include + +#include +#include +#include + +int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR, _In_ int) +{ + // Write results to a log file in the temp directory + wchar_t tempPath[MAX_PATH]{}; + GetTempPathW(MAX_PATH, tempPath); + std::wstring logPath = std::wstring(tempPath) + L"CMake_PackagedSC.log"; + std::wofstream log(logPath, std::ios::trunc); + + try + { + winrt::init_apartment(); + + std::wostringstream message; + message << L"Packaged Self-Contained App (CMake)\n\n"; + + // Check package identity + UINT32 nameLen = 0; + auto rc = GetCurrentPackageFullName(&nameLen, nullptr); + if (rc == APPMODEL_ERROR_NO_PACKAGE) + { + message << L"Package Identity: NONE\n"; + log << L"PackageIdentity=NONE" << std::endl; + } + else + { + std::wstring pkgName(nameLen, L'\0'); + GetCurrentPackageFullName(&nameLen, pkgName.data()); + // Remove trailing null that GetCurrentPackageFullName includes in the length + if (!pkgName.empty() && pkgName.back() == L'\0') + { + pkgName.pop_back(); + } + message << L"Package Identity: " << pkgName << L"\n"; + log << L"PackageIdentity=" << pkgName << std::endl; + } + + // Use WinAppSDK API - try EnvironmentManager, fall back to AppLifecycle + bool apiWorked = false; + + // Try EnvironmentManager + if (winrt::Microsoft::Windows::System::EnvironmentManager::IsSupported()) + { + auto envManager = winrt::Microsoft::Windows::System::EnvironmentManager::GetForProcess(); + auto value = envManager.GetEnvironmentVariable(L"PROCESSOR_ARCHITECTURE"); + message << L"EnvironmentManager: Supported\n"; + message << L"PROCESSOR_ARCHITECTURE: " << std::wstring(value) << L"\n"; + log << L"EnvironmentManager=Supported" << std::endl; + log << L"PROCESSOR_ARCHITECTURE=" << std::wstring(value) << std::endl; + apiWorked = true; + } + else + { + message << L"EnvironmentManager: Not supported\n"; + log << L"EnvironmentManager=NotSupported" << std::endl; + } + + // Always show something from WinAppSDK to prove it loaded + wchar_t arch[64]{}; + GetEnvironmentVariableW(L"PROCESSOR_ARCHITECTURE", arch, 64); + message << L"Win32 PROCESSOR_ARCHITECTURE: " << arch << L"\n"; + + // Use WinAppSDK PowerManager to query battery and power state + namespace Power = winrt::Microsoft::Windows::System::Power; + + message << L"\n"; + auto batteryStatus = Power::PowerManager::BatteryStatus(); + message << L"PowerManager.BatteryStatus: "; + switch (batteryStatus) + { + case Power::BatteryStatus::NotPresent: message << L"NotPresent"; break; + case Power::BatteryStatus::Discharging: message << L"Discharging"; break; + case Power::BatteryStatus::Idle: message << L"Idle"; break; + case Power::BatteryStatus::Charging: message << L"Charging"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + auto powerSupply = Power::PowerManager::PowerSupplyStatus(); + message << L"PowerManager.PowerSupplyStatus: "; + switch (powerSupply) + { + case Power::PowerSupplyStatus::NotPresent: message << L"NotPresent"; break; + case Power::PowerSupplyStatus::Inadequate: message << L"Inadequate"; break; + case Power::PowerSupplyStatus::Adequate: message << L"Adequate"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + message << L"PowerManager.RemainingChargePercent: " + << Power::PowerManager::RemainingChargePercent() << L"%\n"; + + log << L"Result=SUCCESS" << std::endl; + log.close(); + MessageBoxW(nullptr, message.str().c_str(), L"WinAppSDK CMake Test", MB_OK | MB_ICONINFORMATION); + } + catch (winrt::hresult_error const& ex) + { + log << L"Error=0x" << std::hex << ex.code() << std::endl; + log.close(); + std::wostringstream err; + err << L"WinRT error 0x" << std::hex << ex.code() << L"\n" << ex.message().c_str(); + MessageBoxW(nullptr, err.str().c_str(), L"WinAppSDK Error", MB_OK | MB_ICONERROR); + return 1; + } + + return 0; +} diff --git a/Samples/CMake/README.md b/Samples/CMake/README.md new file mode 100644 index 000000000..508d78e1a --- /dev/null +++ b/Samples/CMake/README.md @@ -0,0 +1,120 @@ +# CMake Baseline Tests + +WinAppSDK deployment matrix baseline tests built with **CMake** and **NuGetCMakePackage**. + +## Deployment Matrix + +| | **Packaged (MSIX)** | **Unpackaged** | +|--------------------|----------------------------|-------------------------------| +| **Self-Contained** | PackagedSelfContained | UnpackagedSelfContained | +| **Framework** | PackagedFrameworkDependent | UnpackagedFrameworkDependent | + +Each app uses the WinAppSDK `EnvironmentManager` API to demonstrate SDK functionality. + +## Prerequisites + +- CMake 3.31+ +- Visual Studio 2022 (with C++ desktop workload) +- WinAppSDK runtime (`Microsoft.WindowsAppRuntime.2.0-preview1`) installed for framework-dependent apps + +## Build + +### Visual Studio generator (recommended) + +```powershell +cmake --preset vs2022-x64 +cmake --build --preset vs2022-x64-debug +``` + +### Ninja generator (from VS Developer Command Prompt) + +```powershell +# Step 1: Open x64 Native Tools Command Prompt, or set up the environment: +& "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + +# Step 2: Configure +cmake --preset ninja-x64 + +# Step 3: Build Debug +cmake --build --preset ninja-x64-debug + +# Or build Release +cmake --build --preset ninja-x64-release +``` + +> **Note:** The Ninja presets use a short build directory (`C:\b\nx64`) to avoid Windows path length issues with the auto-generated source files from NuGetCMakePackage. This is a known limitation when NuGet packages are in deep user-profile paths. + +Alternatively, run configure and build in a single command from any shell: + +```powershell +cmd /c "`"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat`" && cmake --preset ninja-x64 && cmake --build --preset ninja-x64-debug" +``` + +Output executables will be in `C:\b\nx64\\Debug\` (or `Release\`). + +## Run + +### Run all tests (recommended) + +```powershell +.\Test-AllApps.ps1 +.\Test-AllApps.ps1 -Configuration Release +``` + +This script builds, registers, launches, and verifies all 4 apps automatically. Packaged apps are verified via log files for package identity. + +### Unpackaged apps (direct launch) + +```powershell +.\build\vs2022-x64\UnpackagedSelfContained\Debug\UnpackagedSelfContained.exe +.\build\vs2022-x64\UnpackagedFrameworkDependent\Debug\UnpackagedFrameworkDependent.exe +``` + +### Packaged apps (use launch script) + +```powershell +.\Launch-PackagedApp.ps1 -AppxManifestPath .\build\vs2022-x64\PackagedSelfContained\Debug\AppxManifest.xml -PackageNameFilter *CMake-PackagedSelfContained* +.\Launch-PackagedApp.ps1 -AppxManifestPath .\build\vs2022-x64\PackagedFrameworkDependent\Debug\AppxManifest.xml -PackageNameFilter *CMake-PackagedFrameworkDependent* + +# Cleanup when done +Get-AppxPackage *CMake-Packaged* | Remove-AppxPackage +``` + +### Verify package identity (check log files) + +```powershell +Get-Content $env:TEMP\CMake_PackagedSC.log +Get-Content $env:TEMP\CMake_PackagedFW.log +``` + +## Project Structure + +``` +CMakeBaselineTests/ +├── CMakeLists.txt # Root — FetchContent + add_nuget_packages +├── CMakePresets.json # VS2022 and Ninja presets +├── Configuration.cmake # Shared compiler settings +├── nuget.config # Points to ../../LocalNugetCache +├── Launch-PackagedApp.ps1 # Script to register + COM-activate packaged apps +├── Test-AllApps.ps1 # Script to build, launch, and verify all 4 apps +├── UnpackagedSelfContained/ +│ ├── CMakeLists.txt # Links Foundation_SelfContained, HybridCRT +│ ├── main.cpp +│ └── README.md +├── UnpackagedFrameworkDependent/ +│ ├── CMakeLists.txt # Links Foundation_Framework + bootstrapper +│ ├── main.cpp +│ └── README.md +├── PackagedSelfContained/ +│ ├── CMakeLists.txt # Links Foundation_SelfContained, HybridCRT +│ ├── main.cpp +│ ├── AppxManifest.xml +│ ├── Logo.png +│ └── README.md +└── PackagedFrameworkDependent/ + ├── CMakeLists.txt # Links Foundation_Framework + ├── main.cpp + ├── AppxManifest.xml # Declares framework package dependency + ├── Logo.png + └── README.md +``` diff --git a/Samples/CMake/Test-AllApps.ps1 b/Samples/CMake/Test-AllApps.ps1 new file mode 100644 index 000000000..faff37e42 --- /dev/null +++ b/Samples/CMake/Test-AllApps.ps1 @@ -0,0 +1,145 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +<# +.SYNOPSIS + Builds, registers, launches, and verifies all 4 CMake baseline test apps. +.DESCRIPTION + Runs the full test cycle: cmake build, launch each app, verify output via log files. + Requires cmake --preset vs2022-x64 to have been run first (configure step). +.EXAMPLE + .\Test-AllApps.ps1 + .\Test-AllApps.ps1 -Configuration Release +#> + +param( + [string]$Configuration = "Debug" +) + +Set-StrictMode -Version 2.0 +$ErrorActionPreference = 'Stop' + +$buildDir = Join-Path $PSScriptRoot "build\vs2022-x64" + +# COM activation type for packaged apps +Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; +using System.Runtime.CompilerServices; + +[ComImport, Guid("2e941141-7f97-4756-ba1d-9decde894a3d"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +public interface IAppActivationTest { + IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] UInt32 options, [Out] out UInt32 processId); +} + +[ComImport, Guid("45BA127D-10A8-46EA-8AB7-56EA9078943C")] +public class AppActivationTest : IAppActivationTest { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + public extern IntPtr ActivateApplication([In] String appUserModelId, [In] String arguments, [In] UInt32 options, [Out] out UInt32 processId); +} +"@ -ErrorAction SilentlyContinue + +# Build +Write-Host "=== Building $Configuration ===" +$presetName = "vs2022-x64-$($Configuration.ToLower())" +cmake --build --preset $presetName +if ($LASTEXITCODE -ne 0) { Write-Error "Build failed"; exit 1 } + +$results = @() + +# --- Test 1: UnpackagedSelfContained --- +Write-Host "" +Write-Host "=== Test: UnpackagedSelfContained ===" +$exe = Join-Path $buildDir "UnpackagedSelfContained\$Configuration\UnpackagedSelfContained.exe" +$p = Start-Process -FilePath $exe -PassThru +Start-Sleep -Seconds 4 +$p.Refresh() +if ($p.HasExited) { + $results += [PSCustomObject]@{ App = "UnpackagedSelfContained"; Status = "FAIL"; Detail = "Exited with $($p.ExitCode)" } +} else { + $results += [PSCustomObject]@{ App = "UnpackagedSelfContained"; Status = "PASS"; Detail = "Running" } + [System.Diagnostics.Process]::GetProcessById($p.Id).Kill() +} + +# --- Test 2: UnpackagedFrameworkDependent --- +Write-Host "" +Write-Host "=== Test: UnpackagedFrameworkDependent ===" +$exe = Join-Path $buildDir "UnpackagedFrameworkDependent\$Configuration\UnpackagedFrameworkDependent.exe" +$p = Start-Process -FilePath $exe -PassThru +Start-Sleep -Seconds 4 +$p.Refresh() +if ($p.HasExited) { + $results += [PSCustomObject]@{ App = "UnpackagedFrameworkDependent"; Status = "FAIL"; Detail = "Exited with $($p.ExitCode)" } +} else { + $results += [PSCustomObject]@{ App = "UnpackagedFrameworkDependent"; Status = "PASS"; Detail = "Running" } + [System.Diagnostics.Process]::GetProcessById($p.Id).Kill() +} + +Start-Sleep -Seconds 2 + +# --- Test 3: PackagedSelfContained --- +Write-Host "" +Write-Host "=== Test: PackagedSelfContained ===" +$manifest = Join-Path $buildDir "PackagedSelfContained\$Configuration\AppxManifest.xml" +$logFile = Join-Path $env:TEMP "CMake_PackagedSC.log" +Remove-Item $logFile -Force -ErrorAction SilentlyContinue + +Get-AppxPackage *CMake-PackagedSelfContained* | Remove-AppxPackage -ErrorAction SilentlyContinue +Start-Sleep -Seconds 3 +Add-AppxPackage -Register $manifest + +$pfn = (Get-AppxPackage *CMake-PackagedSelfContained*).PackageFamilyName +$aam = New-Object AppActivationTest +$pid_out = [uint32]0 +$aam.ActivateApplication("${pfn}!App", $null, 0, [ref]$pid_out) | Out-Null +Start-Sleep -Seconds 5 +Get-Process -Id $pid_out -ErrorAction SilentlyContinue | ForEach-Object { [System.Diagnostics.Process]::GetProcessById($_.Id).Kill() } +Start-Sleep -Seconds 1 + +if ((Test-Path $logFile) -and (Get-Content $logFile -Raw) -match "Result=SUCCESS") { + $identity = (Get-Content $logFile | Select-String "PackageIdentity=(.+)").Matches.Groups[1].Value + $results += [PSCustomObject]@{ App = "PackagedSelfContained"; Status = "PASS"; Detail = $identity } +} else { + $results += [PSCustomObject]@{ App = "PackagedSelfContained"; Status = "FAIL"; Detail = if (Test-Path $logFile) { Get-Content $logFile -Raw } else { "No log" } } +} +Get-AppxPackage *CMake-PackagedSelfContained* | Remove-AppxPackage -ErrorAction SilentlyContinue + +# --- Test 4: PackagedFrameworkDependent --- +Write-Host "" +Write-Host "=== Test: PackagedFrameworkDependent ===" +$manifest = Join-Path $buildDir "PackagedFrameworkDependent\$Configuration\AppxManifest.xml" +$logFile = Join-Path $env:TEMP "CMake_PackagedFW.log" +Remove-Item $logFile -Force -ErrorAction SilentlyContinue + +Get-AppxPackage *CMake-PackagedFrameworkDependent* | Remove-AppxPackage -ErrorAction SilentlyContinue +Start-Sleep -Seconds 3 +Add-AppxPackage -Register $manifest + +$pfn = (Get-AppxPackage *CMake-PackagedFrameworkDependent*).PackageFamilyName +$pid_out = [uint32]0 +$aam.ActivateApplication("${pfn}!App", $null, 0, [ref]$pid_out) | Out-Null +Start-Sleep -Seconds 5 +Get-Process -Id $pid_out -ErrorAction SilentlyContinue | ForEach-Object { [System.Diagnostics.Process]::GetProcessById($_.Id).Kill() } +Start-Sleep -Seconds 1 + +if ((Test-Path $logFile) -and (Get-Content $logFile -Raw) -match "Result=SUCCESS") { + $identity = (Get-Content $logFile | Select-String "PackageIdentity=(.+)").Matches.Groups[1].Value + $results += [PSCustomObject]@{ App = "PackagedFrameworkDependent"; Status = "PASS"; Detail = $identity } +} else { + $results += [PSCustomObject]@{ App = "PackagedFrameworkDependent"; Status = "FAIL"; Detail = if (Test-Path $logFile) { Get-Content $logFile -Raw } else { "No log" } } +} +Get-AppxPackage *CMake-PackagedFrameworkDependent* | Remove-AppxPackage -ErrorAction SilentlyContinue + +# Summary +Write-Host "" +Write-Host "=== RESULTS ===" +$results | Format-Table -AutoSize + +$failed = ($results | Where-Object { $_.Status -eq "FAIL" }).Count +if ($failed -gt 0) { + Write-Host "$failed test(s) FAILED" -ForegroundColor Red + exit 1 +} else { + Write-Host "All tests PASSED" -ForegroundColor Green + exit 0 +} diff --git a/Samples/CMake/UnpackagedFrameworkDependent/CMakeLists.txt b/Samples/CMake/UnpackagedFrameworkDependent/CMakeLists.txt new file mode 100644 index 000000000..0a88090d1 --- /dev/null +++ b/Samples/CMake/UnpackagedFrameworkDependent/CMakeLists.txt @@ -0,0 +1,22 @@ +#---------------------------------------------------------------------------------------------------------------------- +# UnpackagedFrameworkDependent - Framework-dependent deployment, no MSIX packaging +#---------------------------------------------------------------------------------------------------------------------- +project(UnpackagedFrameworkDependent LANGUAGES CXX) + +add_executable(UnpackagedFrameworkDependent WIN32 + main.cpp +) + +# Framework-dependent: use dynamic CRT +set_target_properties(UnpackagedFrameworkDependent PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>DLL" + # Enable bootstrapper auto-init to load the framework package at startup + WindowsAppSdkBootstrapInitialize TRUE +) + +target_link_libraries(UnpackagedFrameworkDependent + PRIVATE + Microsoft.WindowsAppSDK.Foundation_Framework +) + +post_build_runtime_dll_copy(UnpackagedFrameworkDependent) diff --git a/Samples/CMake/UnpackagedFrameworkDependent/README.md b/Samples/CMake/UnpackagedFrameworkDependent/README.md new file mode 100644 index 000000000..c2b5fd659 --- /dev/null +++ b/Samples/CMake/UnpackagedFrameworkDependent/README.md @@ -0,0 +1,57 @@ +# UnpackagedFrameworkDependent + +A native C++ WinAppSDK app that is **unpackaged** and **framework-dependent**, built with CMake and NuGetCMakePackage. + +## What this app demonstrates + +- Unpackaged desktop deployment (no MSIX, no package identity) +- Framework-dependent WinAppSDK deployment — requires the WinAppSDK runtime to be installed on the machine +- Standard dynamic CRT linking +- Automatic bootstrapper initialization via the Foundation_Framework target +- WinAppSDK EnvironmentManager API via C++/WinRT projections + +## Configuration + +- CMake target: `Microsoft.WindowsAppSDK.Foundation_Framework` +- CMake target property: `WindowsAppSdkBootstrapInitialize = TRUE` +- CRT: `MultiThreadedDLL` / `MultiThreadedDebugDLL` (standard dynamic CRT) +- No MSIX packaging, no AppxManifest + +## Build + +From the `CMakeBaselineTests/` directory: + +```powershell +# Configure (one-time) +cmake --preset vs2022-x64 + +# Build +cmake --build --preset vs2022-x64-debug +``` + +Or for Release: + +```powershell +cmake --build --preset vs2022-x64-release +``` + +## Launch + +Run the executable directly: + +```powershell +.\build\vs2022-x64\UnpackagedFrameworkDependent\Debug\UnpackagedFrameworkDependent.exe +``` + +**Expected result:** A message box showing "Unpackaged Framework-Dependent App (CMake)" with EnvironmentManager status and the `PROCESSOR_ARCHITECTURE` value. + +## Prerequisites + +- **WinAppSDK runtime** (`Microsoft.WindowsAppRuntime.2.0-preview1`) must be installed on the machine +- **Visual C++ Redistributable** must be installed (typically already present) + +## Notes + +- The bootstrapper (`MddBootstrapAutoInitializer.cpp`) is automatically compiled and linked by the Foundation_Framework target when `WindowsAppSdkBootstrapInitialize` is set to `TRUE`. +- The bootstrapper locates and loads the WinAppSDK Framework package dynamically at runtime. +- Unlike self-contained, the WinAppSDK binaries are NOT bundled — smaller deployment but requires the runtime installer on the target machine. diff --git a/Samples/CMake/UnpackagedFrameworkDependent/main.cpp b/Samples/CMake/UnpackagedFrameworkDependent/main.cpp new file mode 100644 index 000000000..3678efef6 --- /dev/null +++ b/Samples/CMake/UnpackagedFrameworkDependent/main.cpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include + +#include +#include +#include + +int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR, _In_ int) +{ + // Bootstrap initialization is handled automatically by the WinAppSDK build targets + // via MddBootstrapAutoInitializer.cpp linked through Foundation_Framework target + try + { + winrt::init_apartment(); + + std::wostringstream message; + message << L"Unpackaged Framework-Dependent App (CMake)\n\n"; + + if (winrt::Microsoft::Windows::System::EnvironmentManager::IsSupported()) + { + auto envManager = winrt::Microsoft::Windows::System::EnvironmentManager::GetForProcess(); + auto value = envManager.GetEnvironmentVariable(L"PROCESSOR_ARCHITECTURE"); + message << L"EnvironmentManager: Supported\n"; + message << L"PROCESSOR_ARCHITECTURE: " << std::wstring(value) << L"\n"; + } + else + { + message << L"EnvironmentManager: Not supported on this OS version\n"; + } + + // Use WinAppSDK PowerManager to query battery and power state + namespace Power = winrt::Microsoft::Windows::System::Power; + + message << L"\n"; + auto batteryStatus = Power::PowerManager::BatteryStatus(); + message << L"PowerManager.BatteryStatus: "; + switch (batteryStatus) + { + case Power::BatteryStatus::NotPresent: message << L"NotPresent"; break; + case Power::BatteryStatus::Discharging: message << L"Discharging"; break; + case Power::BatteryStatus::Idle: message << L"Idle"; break; + case Power::BatteryStatus::Charging: message << L"Charging"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + auto powerSupply = Power::PowerManager::PowerSupplyStatus(); + message << L"PowerManager.PowerSupplyStatus: "; + switch (powerSupply) + { + case Power::PowerSupplyStatus::NotPresent: message << L"NotPresent"; break; + case Power::PowerSupplyStatus::Inadequate: message << L"Inadequate"; break; + case Power::PowerSupplyStatus::Adequate: message << L"Adequate"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + message << L"PowerManager.RemainingChargePercent: " + << Power::PowerManager::RemainingChargePercent() << L"%\n"; + + MessageBoxW(nullptr, message.str().c_str(), L"WinAppSDK CMake Test", MB_OK | MB_ICONINFORMATION); + } + catch (winrt::hresult_error const& ex) + { + std::wostringstream err; + err << L"WinRT error 0x" << std::hex << ex.code() << L"\n" << ex.message().c_str(); + MessageBoxW(nullptr, err.str().c_str(), L"WinAppSDK Error", MB_OK | MB_ICONERROR); + return 1; + } + + return 0; +} diff --git a/Samples/CMake/UnpackagedSelfContained/CMakeLists.txt b/Samples/CMake/UnpackagedSelfContained/CMakeLists.txt new file mode 100644 index 000000000..fdfcb009d --- /dev/null +++ b/Samples/CMake/UnpackagedSelfContained/CMakeLists.txt @@ -0,0 +1,21 @@ +#---------------------------------------------------------------------------------------------------------------------- +# UnpackagedSelfContained - Self-contained deployment, no MSIX packaging +#---------------------------------------------------------------------------------------------------------------------- +project(UnpackagedSelfContained LANGUAGES CXX) + +add_executable(UnpackagedSelfContained WIN32 + main.cpp +) + +# Self-contained: use static VC runtime (Hybrid CRT) +set_target_properties(UnpackagedSelfContained PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" +) + +target_link_libraries(UnpackagedSelfContained + PRIVATE + Microsoft.WindowsAppSDK.Foundation_SelfContained + Microsoft.WindowsAppSDK.InteractiveExperiences_SelfContained +) + +post_build_runtime_dll_copy(UnpackagedSelfContained) diff --git a/Samples/CMake/UnpackagedSelfContained/README.md b/Samples/CMake/UnpackagedSelfContained/README.md new file mode 100644 index 000000000..0665df175 --- /dev/null +++ b/Samples/CMake/UnpackagedSelfContained/README.md @@ -0,0 +1,51 @@ +# UnpackagedSelfContained + +A native C++ WinAppSDK app that is **unpackaged** and **self-contained**, built with CMake and NuGetCMakePackage. + +## What this app demonstrates + +- Unpackaged desktop deployment (no MSIX, no package identity) +- Self-contained WinAppSDK deployment — all WinAppSDK DLLs are copied alongside the executable +- Hybrid CRT (static VC runtime + dynamic UCRT) +- WinAppSDK EnvironmentManager API via C++/WinRT projections +- UndockedRegFreeWinRT initialization handled by the Foundation_SelfContained target's manifest + +## Configuration + +- CMake target: `Microsoft.WindowsAppSDK.Foundation_SelfContained` +- CRT: `MultiThreaded` / `MultiThreadedDebug` (Hybrid CRT) +- No MSIX packaging, no AppxManifest + +## Build + +From the `CMakeBaselineTests/` directory: + +```powershell +# Configure (one-time) +cmake --preset vs2022-x64 + +# Build +cmake --build --preset vs2022-x64-debug +``` + +Or for Release: + +```powershell +cmake --build --preset vs2022-x64-release +``` + +## Launch + +Run the executable directly: + +```powershell +.\build\vs2022-x64\UnpackagedSelfContained\Debug\UnpackagedSelfContained.exe +``` + +**Expected result:** A message box showing "Unpackaged Self-Contained App (CMake)" with EnvironmentManager status and the `PROCESSOR_ARCHITECTURE` value. + +## Notes + +- Since this is self-contained, all WinAppSDK DLLs are in the output directory alongside the executable. +- The app can be xcopy-deployed to another machine without requiring the WinAppSDK runtime installer. +- No bootstrapper or MSIX registration needed — the simplest deployment scenario. diff --git a/Samples/CMake/UnpackagedSelfContained/main.cpp b/Samples/CMake/UnpackagedSelfContained/main.cpp new file mode 100644 index 000000000..e74563ad3 --- /dev/null +++ b/Samples/CMake/UnpackagedSelfContained/main.cpp @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include +#include +#include + +#include +#include +#include + +int WINAPI wWinMain(_In_ HINSTANCE, _In_opt_ HINSTANCE, _In_ PWSTR, _In_ int) +{ + try + { + winrt::init_apartment(); + + std::wostringstream message; + message << L"Unpackaged Self-Contained App (CMake)\n\n"; + + if (winrt::Microsoft::Windows::System::EnvironmentManager::IsSupported()) + { + auto envManager = winrt::Microsoft::Windows::System::EnvironmentManager::GetForProcess(); + auto value = envManager.GetEnvironmentVariable(L"PROCESSOR_ARCHITECTURE"); + message << L"EnvironmentManager: Supported\n"; + message << L"PROCESSOR_ARCHITECTURE: " << std::wstring(value) << L"\n"; + } + else + { + message << L"EnvironmentManager: Not supported on this OS version\n"; + } + + // Use WinAppSDK PowerManager to query battery and power state + namespace Power = winrt::Microsoft::Windows::System::Power; + + message << L"\n"; + auto batteryStatus = Power::PowerManager::BatteryStatus(); + message << L"PowerManager.BatteryStatus: "; + switch (batteryStatus) + { + case Power::BatteryStatus::NotPresent: message << L"NotPresent"; break; + case Power::BatteryStatus::Discharging: message << L"Discharging"; break; + case Power::BatteryStatus::Idle: message << L"Idle"; break; + case Power::BatteryStatus::Charging: message << L"Charging"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + auto powerSupply = Power::PowerManager::PowerSupplyStatus(); + message << L"PowerManager.PowerSupplyStatus: "; + switch (powerSupply) + { + case Power::PowerSupplyStatus::NotPresent: message << L"NotPresent"; break; + case Power::PowerSupplyStatus::Inadequate: message << L"Inadequate"; break; + case Power::PowerSupplyStatus::Adequate: message << L"Adequate"; break; + default: message << L"Unknown"; break; + } + message << L"\n"; + + message << L"PowerManager.RemainingChargePercent: " + << Power::PowerManager::RemainingChargePercent() << L"%\n"; + + MessageBoxW(nullptr, message.str().c_str(), L"WinAppSDK CMake Test", MB_OK | MB_ICONINFORMATION); + } + catch (winrt::hresult_error const& ex) + { + std::wostringstream err; + err << L"WinRT error 0x" << std::hex << ex.code() << L"\n" << ex.message().c_str(); + MessageBoxW(nullptr, err.str().c_str(), L"WinAppSDK Error", MB_OK | MB_ICONERROR); + return 1; + } + + return 0; +}