teamsiso/src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj
Zac Gaetano 166e7d6e6a
Some checks failed
CI / build-and-test (push) Has been cancelled
build(winui3): switch to WindowsAppSDK 1.8 + add diagnostic probe
Two big findings from a custom MddBootstrapInitialize2 P/Invoke probe
this session:

1. The original WinUI 3 activation failure ("this application could not
   be started") was MDD_E_BOOTSTRAP_INITIALIZE_DDLM_NOT_FOUND (HR
   0x80670016). The framework package Microsoft.WindowsAppRuntime.1.6
   was installed, but the Dynamic Dependency Lifetime Manager sibling
   (MicrosoftCorporationII.WinAppRuntime.Main.1.6) wasn't. This machine
   has Main.1.5 and Main.1.8 packages but no Main.1.6, so bootstrap for
   1.6 fails.

2. Switching the WindowsAppSDK NuGet to 1.8.250916003 + the bootstrap
   major.minor to 0x00010008 in Program.cs gets past activation. The
   .exe now launches and Bootstrap.TryInitialize returns S_OK. The 1.8
   DDLM is present and the runtime spins up.

Also lands `src/TeamsISO.App.WinUI.Probe/`, a tiny console diagnostic
that calls MddBootstrapInitialize2 directly via P/Invoke (bypassing the
full WindowsAppSDK NuGet to avoid the MRT/PRI MSBuild tasks that need
VS's AppxPackage tooling installed). The probe prints the HResult and a
human-readable description; use it to triage WindowsAppSDK activation
on any deployment target:

  dotnet run --project src/TeamsISO.App.WinUI.Probe

A SECOND ISSUE surfaces after activation: the .exe crashes 1 second
after launch with 0xC000027B inside Microsoft.UI.Xaml.dll, sub-code
0x802b000a (XAML_E_PARSER_GENERAL_ERROR). The participants ItemsRepeater
with {Binding ...} markup is suspect (WinUI 3 prefers x:Bind with
x:DataType, and Visibility="{Binding bool}" needs a converter). The
ItemsRepeater is stubbed out to a plain "Participants list renders here"
TextBlock placeholder for now; same crash recurs, so the XAML issue is
elsewhere — likely in Controls.xaml (one of CharacterSpacing /
TextCaption / etc. unsupported), in App.xaml's MergedDictionary chain,
or in MainWindow.xaml's Storyboard target.

Triaging the XAML parse error is the next session's first action. The
sub-code 0x802b000a will help (search WindowsAppSDK source for the
matching XAML parser error). The migration plan in
docs/superpowers/plans/2026-05-12-winui3-migration.md is updated.

Build remains clean.
2026-05-13 00:39:43 -04:00

146 lines
7.7 KiB
XML

<Project Sdk="Microsoft.NET.Sdk">
<!--
TeamsISO WinUI 3 host. Coexists with the WPF project (src/TeamsISO.App)
during the redesign migration. Shares the engine (TeamsISO.Engine) and
the NDI interop assembly via ProjectReference. Once the WinUI 3 build is
feature-complete and tested against a real Teams meeting, the WPF
project is retired and this becomes the only shipping host.
Target framework choice: net8.0-windows10.0.19041.0 is the minimum the
Windows App SDK supports cleanly. Going higher (e.g. 22621) would lock
out Win10 1809+ operators, which is undesirable for a broadcast tool
that still has to run on hardware in working broadcast suites.
Packaging mode: WindowsPackageType=None for "unpackaged" — the .exe
drops directly into Program Files via the existing MSI rather than
going through MSIX. The Windows App Runtime install becomes a prereq
of the MSI (or bootstrapped at startup), which matches how operators
install NDI Runtime today.
-->
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.19041.0</TargetFramework>
<TargetPlatformMinVersion>10.0.17763.0</TargetPlatformMinVersion>
<SupportedOSPlatformVersion>10.0.17763.0</SupportedOSPlatformVersion>
<RootNamespace>TeamsISO.App.WinUI</RootNamespace>
<AssemblyName>TeamsISO</AssemblyName>
<!--
Default app.manifest deferred: WinUI 3 emits its own manifest with the
DPI awareness + supportedOS GUIDs that match what we want. Our custom
manifest (kept in tree at app.manifest) describes the same intent but
doesn't currently merge cleanly with the framework-emitted manifest;
see docs/superpowers/plans/2026-05-12-winui3-migration.md for the
follow-up to reintroduce it via uap:VisualElements.
-->
<Platforms>x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x64;win-arm64</RuntimeIdentifiers>
<UseWinUI>true</UseWinUI>
<WindowsPackageType>None</WindowsPackageType>
<EnableMsixTooling>true</EnableMsixTooling>
<!--
Pinning the Windows SDK projection package: WindowsAppSDK 1.6 requires
Microsoft.Windows.SDK.NET.Ref >= 10.0.19041.38, but the .NET 8.0.301
SDK installed here ships an older Ref. Setting this explicitly avoids
having to upgrade the .NET SDK on the build host.
-->
<WindowsSdkPackageVersion>10.0.19041.38</WindowsSdkPackageVersion>
<!--
Disable the XAML compiler's auto-generated Program.Main so we can write
one that bootstraps the Windows App Runtime explicitly. The default
generated Main calls Application.Start directly, with no Bootstrap
initialization step — that's fine for packaged MSIX apps but blocks
unpackaged launch on a machine where the runtime is installed only as
a framework package. Program.cs in this project takes ownership of
Main and calls Bootstrap.TryInitialize(0x00010006) before Start.
-->
<DefineConstants>$(DefineConstants);DISABLE_XAML_GENERATED_MAIN</DefineConstants>
<!--
RuntimeIdentifier locks the build to win-x64 so runtime DLLs from
runtimes/win-x64/native (Microsoft.WindowsAppRuntime.Bootstrap.dll,
WebView2Loader.dll) flatten into the output dir alongside the .exe.
Without this, those DLLs sit in runtimes/win-x64/native/ and the
loader doesn't find them at activation time.
-->
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<!--
Suppress the auto-injected UndockedRegFreeWinRT ModuleInitializer.
The bundled initializer P/Invokes Microsoft.WindowsAppRuntime.dll
during module load, BEFORE our Program.Main has a chance to call
Bootstrap.TryInitialize. Since the runtime DLL lives in the framework
MSIX package (not the output dir) on a framework-dependent install,
the P/Invoke fails to locate it and the .exe dies with the generic
"this application could not be started" dialog — diagnosed by
following the Microsoft.WindowsAppSDK.UndockedRegFreeWinRT.CS.targets
chain and reading the auto-init source. Our Program.cs handles the
bootstrap explicitly in the right order.
-->
<WindowsAppSdkUndockedRegFreeWinRTInitialize>false</WindowsAppSdkUndockedRegFreeWinRTInitialize>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<ApplicationIcon>Assets\teamsiso.ico</ApplicationIcon>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<!--
WindowsAppSDK 1.8 is the version whose DDLM is installed on the build
host (verified via Get-AppxPackage MicrosoftCorporationII.WinAppRuntime.
Main.1.8). 1.6's framework package is present, but its DDLM sibling
isn't — bootstrap returns MDD_E_BOOTSTRAP_INITIALIZE_DDLM_NOT_FOUND.
See docs/superpowers/work-log-2026-05-12.md for the full diagnosis.
DataGrid lives in the older 7.x Community Toolkit because the 8.x line
dropped it; 7.1.2 still works against WindowsAppSDK 1.8 and is the
only currently-maintained free DataGrid for this stack.
-->
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.8.250916003" />
<PackageReference Include="CommunityToolkit.WinUI.UI.Controls.DataGrid" Version="7.1.2" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\TeamsISO.Engine\TeamsISO.Engine.csproj" />
<ProjectReference Include="..\TeamsISO.Engine.NdiInterop\TeamsISO.Engine.NdiInterop.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Assets\teamsiso.ico" />
<Content Include="Assets\dragon-mark.png" />
<Content Include="Assets\wild-dragon-wordmark.png" />
<Content Include="Assets\Fonts\Inter.ttf" />
<Content Include="Assets\Fonts\JetBrainsMono.ttf" />
</ItemGroup>
<!--
Post-build runtimeconfig patch. .NET 8 SDK adds Microsoft.WindowsDesktop.App
as an implicit framework reference for any -windows target framework moniker.
WinUI 3 doesn't use that framework; including it in runtimeconfig.json
forces the .NET host to resolve and load WindowsDesktop.App at startup,
which contributes to the WindowsAppSDK activation chain on unpackaged
apps. This target rewrites the generated runtimeconfig.json to drop the
WindowsDesktop.App entry so the host loads only NETCore.App.
The target runs after the framework-dependent build copies the
runtimeconfig.json to the output dir, before the bin/Debug/.../win-x64/
directory is final. It's a string-level rewrite — sufficient because the
JSON shape is stable across SDK versions and the WindowsDesktop.App
entry has a deterministic indent + sibling structure.
-->
<Target Name="StripWindowsDesktopAppFromRuntimeConfig" AfterTargets="GenerateBuildRuntimeConfigurationFiles">
<PropertyGroup>
<_RuntimeConfigPath>$(OutDir)$(AssemblyName).runtimeconfig.json</_RuntimeConfigPath>
</PropertyGroup>
<PropertyGroup Condition="Exists('$(_RuntimeConfigPath)')">
<_RuntimeConfigContent>$([System.IO.File]::ReadAllText('$(_RuntimeConfigPath)'))</_RuntimeConfigContent>
<_PatchedContent>$([System.Text.RegularExpressions.Regex]::Replace($(_RuntimeConfigContent), ',\s*\{\s*&quot;name&quot;:\s*&quot;Microsoft\.WindowsDesktop\.App&quot;[^\}]*\}', ''))</_PatchedContent>
</PropertyGroup>
<WriteLinesToFile Condition="Exists('$(_RuntimeConfigPath)') and '$(_RuntimeConfigContent)' != '$(_PatchedContent)'"
File="$(_RuntimeConfigPath)"
Lines="$(_PatchedContent)"
Overwrite="true"/>
<Message Condition="Exists('$(_RuntimeConfigPath)') and '$(_RuntimeConfigContent)' != '$(_PatchedContent)'"
Text="Stripped Microsoft.WindowsDesktop.App from $(_RuntimeConfigPath)"
Importance="high"/>
</Target>
</Project>