dragon-iso/src/TeamsISO.App.WinUI/TeamsISO.App.WinUI.csproj
Zac Gaetano 2f9f7092ed build(winui3): post-build target to strip WindowsDesktop.App from runtimeconfig
Adds a StripWindowsDesktopAppFromRuntimeConfig MSBuild target that
runs after GenerateBuildRuntimeConfigurationFiles and rewrites
TeamsISO.runtimeconfig.json to drop the implicit
Microsoft.WindowsDesktop.App framework reference. The .NET 8 SDK adds
that framework for any -windows TFM, but WinUI 3 doesn't use it and
including it in the runtimeconfig contributes to the broken
unpackaged activation path (one of the three suspects in the migration
plan's Phase 3).

Build log confirms the strip on every build:
  Stripped Microsoft.WindowsDesktop.App from .../TeamsISO.runtimeconfig.json

Verified the runtimeconfig now reads:
  { "name": "Microsoft.NETCore.App", "version": "8.0.0" }
  (no WindowsDesktop.App entry)

This didn't resolve the activation dialog on its own, but it's a
required step for any unpackaged WinUI 3 build and the next debugging
session can rule it out as a contributing cause.
2026-05-13 00:21:33 -04:00

142 lines
7.4 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.6 is the current LTS branch (Win10 1809-compatible at
a 10.0.17763 floor, which matches our SupportedOSPlatformVersion).
DataGrid lives in the older 7.x Community Toolkit because the 8.x line
dropped it; 7.1.2 still works on WinUI 3 / WindowsAppSDK 1.6 and is the
only currently-maintained free DataGrid for this stack.
-->
<ItemGroup>
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.6.250602001" />
<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>