Skip to content

feat(ui-windows-winui): real Windows App SDK bootstrap probe (#4680 step 2) - #4896

Merged
proggeramlug merged 1 commit into
mainfrom
feat/4680-winui-bootstrap
Jun 10, 2026
Merged

feat(ui-windows-winui): real Windows App SDK bootstrap probe (#4680 step 2)#4896
proggeramlug merged 1 commit into
mainfrom
feat/4680-winui-bootstrap

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

What

Implements step 2 of #4680 (opt-in WinUI 3 / Fluent Windows target): a real Windows App SDK bootstrap probe.

--target windows-winui already builds, links, and runs today — the perry-ui-windows-winui crate re-exports the Win32 backend and shares its Fluent DWM chrome (Mica, rounded corners, theme-aware title bar). The one piece still stubbed was the Windows App SDK bootstrap: winui::bootstrap::initialize() unconditionally returned Ready whether or not the runtime existed. This turns it into an honest runtime probe — the gate every future XAML control (step 3) checks before constructing a Microsoft.UI.Xaml object.

How

crates/perry-ui-windows-winui/src/winui.rs now resolves the bootstrapper at runtime via dynamic loading:

  • LoadLibraryW("Microsoft.WindowsAppRuntime.Bootstrap.dll") + GetProcAddress
  • calls MddBootstrapInitialize2 (falls back to MddBootstrapInitialize on older SDKs)
  • S_OKInitStatus::Ready; missing DLL / missing entry points / failure HRESULT → InitStatus::RuntimeMissing, so the caller falls back to Win32 instead of crashing.

Why dynamic loading, not a link dependency: importing Microsoft.WindowsAppRuntime.Bootstrap.lib would make every windows-winui binary hard-require the SDK at load time and fail to start without it. Dynamic loading preserves Perry's single self-contained .exe model — the binary runs on a host without the SDK and degrades to Win32. No new cargo dependency is added; the entry points are bound with raw extern "system" against kernel32, mirroring the existing dwm.rs / crash_handler FFI style.

Other details:

  • Result memoized in an AtomicU8 (runtime is process-wide, initialized at most once).
  • Target SDK release defaults to 1.6, overridable at runtime via PERRY_WINAPPSDK_VERSION="major.minor".
  • PACKAGE_VERSION passed by value as u64 (ABI-identical to the single-UINT64 union on x64); empty versionTag (stable channel); minVersion 0.
  • Off Windows the probe is a compile-time RuntimeMissing (the crate still builds everywhere for host tooling).

Testing

  • cargo test -p perry-ui-windows-winui → green. initialize() is total (never panics) and idempotent on any host; off Windows it must report RuntimeMissing.
  • RuntimeMissing path verified on this Windows host (no Windows App SDK installed → LoadLibraryW returns null). The Ready path runs only where the runtime is installed, so it is exercised on SDK-equipped hosts rather than CI.

Scope / not in this PR

Win32 (--target windows) remains the default and is untouched. Step 3 (the actual XAML widget mapping that consults this probe) is still ahead — this PR lands the foundational gate it needs.

…tep 2)

winui::bootstrap::initialize() was a no-op that always returned Ready. Replace
it with a real runtime probe: dynamically load
Microsoft.WindowsAppRuntime.Bootstrap.dll (LoadLibraryW + GetProcAddress) and
call MddBootstrapInitialize2 (falling back to MddBootstrapInitialize). S_OK =>
Ready; missing DLL / missing entry points / failure HRESULT => RuntimeMissing,
so callers fall back to Win32 instead of crashing.

Dynamic loading (not a link-time import of the bootstrap .lib) preserves Perry's
single self-contained .exe model: a windows-winui binary starts on a host
without the Windows App SDK and degrades to Win32 rather than failing to load.
No new cargo dependency - bound via raw extern "system" against kernel32,
mirroring dwm.rs / crash_handler. Result memoized in an AtomicU8; SDK release
defaults to 1.6, overridable via PERRY_WINAPPSDK_VERSION. Off Windows the probe
is a compile-time RuntimeMissing.

Tests: initialize() is total + idempotent on any host; off Windows it reports
RuntimeMissing. RuntimeMissing path verified on a host without the SDK
(LoadLibraryW returns null); the Ready path runs only where the runtime is
installed.
@proggeramlug
proggeramlug merged commit aee4e82 into main Jun 10, 2026
12 of 13 checks passed
@proggeramlug
proggeramlug deleted the feat/4680-winui-bootstrap branch June 10, 2026 10:05
proggeramlug pushed a commit that referenced this pull request Jun 10, 2026
…#4680)

The backend probe could say Ready/Missing but not *why* it was missing. Capture
the bootstrap outcome detail (sentinels for DLL-missing / no-entry-point /
success, otherwise the raw MddBootstrapInitialize HRESULT) and print it under
PERRY_WINUI_DIAG: e.g. "render backend: fluent (bootstrap detail: ready)" or
"win32 (bootstrap detail: MddBootstrapInitialize failed, HRESULT 0x80670016)".

This made the step-2 success path verifiable against the real runtime: with the
Windows App SDK 1.6 dev NuGet restored (bootstrap DLL next to the exe) and the
DDLM/Main/Singleton runtime packages registered, initialize() flips from
RuntimeMissing to Ready/fluent - so both arms of the merged MddBootstrapInitialize2
FFI (#4896) are now exercised. The 0x80670016 case was exactly a registered
framework package with no DDLM, which the new diagnostic pinpoints.

Adds a unit test for the detail formatter (pure function, environment-agnostic).
proggeramlug pushed a commit that referenced this pull request Jun 15, 2026
…4680 step 3)

Builds on the merged step-2 bootstrap probe (#4896). Add winui::backend, the
single decision point the per-widget WinUI 3 mapping reads: active() returns
RenderBackend::Fluent when the bootstrap probe is Ready, else
RenderBackend::Win32.

Register a CRT static initializer (.CRT$XCU, anchored with #[used]) that runs
the probe at process start for any binary linking this staticlib - i.e. exactly
the --target windows-winui builds, never the default --target windows builds
(which don't link the crate) - so the first widget construction reads an
already-resolved backend. PERRY_WINUI_DIAG prints the chosen backend at launch.
The initializer never panics (runs before main).

Verified on a Windows host without the SDK: the initializer fires in a linked
binary ([perry-winui] render backend: win32 observed via PERRY_WINUI_DIAG), the
.CRT$XCU section is present in the WHOLEARCHIVE'd perry_ui_windows_winui.lib, and
active() mirrors the bootstrap verdict and is stable.

Real Microsoft.UI.Xaml controls still need the WinAppSDK winmd projections +
runtime and are the next step; today the seam always resolves to Win32. Default
--target windows unaffected.
proggeramlug pushed a commit that referenced this pull request Jun 15, 2026
…#4680)

The backend probe could say Ready/Missing but not *why* it was missing. Capture
the bootstrap outcome detail (sentinels for DLL-missing / no-entry-point /
success, otherwise the raw MddBootstrapInitialize HRESULT) and print it under
PERRY_WINUI_DIAG: e.g. "render backend: fluent (bootstrap detail: ready)" or
"win32 (bootstrap detail: MddBootstrapInitialize failed, HRESULT 0x80670016)".

This made the step-2 success path verifiable against the real runtime: with the
Windows App SDK 1.6 dev NuGet restored (bootstrap DLL next to the exe) and the
DDLM/Main/Singleton runtime packages registered, initialize() flips from
RuntimeMissing to Ready/fluent - so both arms of the merged MddBootstrapInitialize2
FFI (#4896) are now exercised. The 0x80670016 case was exactly a registered
framework package with no DDLM, which the new diagnostic pinpoints.

Adds a unit test for the detail formatter (pure function, environment-agnostic).
proggeramlug added a commit that referenced this pull request Jun 15, 2026
…4680 step 3) (#4897)

* feat(ui-windows-winui): render-backend dispatch seam + startup probe (#4680 step 3)

Builds on the merged step-2 bootstrap probe (#4896). Add winui::backend, the
single decision point the per-widget WinUI 3 mapping reads: active() returns
RenderBackend::Fluent when the bootstrap probe is Ready, else
RenderBackend::Win32.

Register a CRT static initializer (.CRT$XCU, anchored with #[used]) that runs
the probe at process start for any binary linking this staticlib - i.e. exactly
the --target windows-winui builds, never the default --target windows builds
(which don't link the crate) - so the first widget construction reads an
already-resolved backend. PERRY_WINUI_DIAG prints the chosen backend at launch.
The initializer never panics (runs before main).

Verified on a Windows host without the SDK: the initializer fires in a linked
binary ([perry-winui] render backend: win32 observed via PERRY_WINUI_DIAG), the
.CRT$XCU section is present in the WHOLEARCHIVE'd perry_ui_windows_winui.lib, and
active() mirrors the bootstrap verdict and is stable.

Real Microsoft.UI.Xaml controls still need the WinAppSDK winmd projections +
runtime and are the next step; today the seam always resolves to Win32. Default
--target windows unaffected.

* feat(ui-windows-winui): surface bootstrap HRESULT in PERRY_WINUI_DIAG (#4680)

The backend probe could say Ready/Missing but not *why* it was missing. Capture
the bootstrap outcome detail (sentinels for DLL-missing / no-entry-point /
success, otherwise the raw MddBootstrapInitialize HRESULT) and print it under
PERRY_WINUI_DIAG: e.g. "render backend: fluent (bootstrap detail: ready)" or
"win32 (bootstrap detail: MddBootstrapInitialize failed, HRESULT 0x80670016)".

This made the step-2 success path verifiable against the real runtime: with the
Windows App SDK 1.6 dev NuGet restored (bootstrap DLL next to the exe) and the
DDLM/Main/Singleton runtime packages registered, initialize() flips from
RuntimeMissing to Ready/fluent - so both arms of the merged MddBootstrapInitialize2
FFI (#4896) are now exercised. The 0x80670016 case was exactly a registered
framework package with no DDLM, which the new diagnostic pinpoints.

Adds a unit test for the detail formatter (pure function, environment-agnostic).

* style(ui-windows-winui): rustfmt import ordering in winui.rs (fix lint)

* chore: sync Cargo.lock to 0.5.1172 (rebase on main)

---------

Co-authored-by: Ralph Kuepper <ralph@skelpo.com>
Co-authored-by: Ralph Küpper <ralph2@skelpo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant