What
When winapp run --debug-output captures a crash dump from a WinUI app, the resulting stack is fairly thin — typically the user's faulting frame plus a couple of cswinrt ABI shims. There's no visibility into the XAML dispatcher / CoreMessaging / runtime chain that actually led to the failure, and no breakdown of stowed exceptions (0xC000027B) which are the most common WinUI crash shape.
Why
Most WinUI crashes originate inside a XAML event handler and surface as a stowed exception wrapping a managed exception. The minimum context a developer needs to diagnose these is:
- The originating HRESULT and its ErrorContext chain
- The full native dispatch stack (Microsoft.UI.Xaml → CXcpDispatcher → CoreMessagingXP → CLR host)
- The managed user frame that threw
Today only the last one is reliably produced.
Potential approach
The WinUI team ships a WinDbg JavaScript extension (winui-dbgext.js in microsoft/microsoft-ui-xaml) that already produces exactly this output via !xamltriage and !xamlstowed. It runs against a minidump and works well with public symbols.
A viable integration shape that avoids requiring a WinDbg install:
- Host DbgEng directly via the
Microsoft.Debugging.Platform.DbgEng NuGet package (winapp already uses Microsoft.Diagnostics.Runtime.Utilities for its native fallback, so the API surface is familiar).
- Add the
Microsoft.Debugging.Platform.SymSrv package and co-locate symsrv.dll next to dbgeng.dll — without this, srv* paths silently no-op and symbols never download. This is the main gotcha.
- Add
Microsoft.Debugging.Platform.DbgX for dbgmodel.dll / msdia140.dll.
- Acquire
JsProvider.dll (the JS scripting host for .scriptload) — this is not distributed on NuGet; it ships only with Debugging Tools for Windows. Options: bundle it, or download a WinDbg/SDK package on first use.
- Fetch
winui-dbgext.js from the public WinUI repo (pin a SHA).
- After the existing ClrMD pass, run
.scriptload winui-dbgext.js; !xamlstowed; !xamltriage against the same dump and append the output to the debug log.
Gating it behind a flag (or auto-enabling when Microsoft.UI.Xaml.dll is in the module list) keeps it scoped to WinUI apps.
Notes for whoever picks this up
!xamlstowed produces the most valuable output and works fully with the NuGet + JsProvider setup above.
!xamltriage's managed-frame walker depends on SOS / CLR data access being loaded in the session; without that it logs CORDBG_E_MISSING_METADATA for those frames. The headline summary (error code, message, user frames, ErrorContext) still comes through. Easiest path is probably to combine winapp's existing ClrMD managed stack with the extension's native output.
- Relevant existing code:
src/winapp-CLI/WinApp.Cli/Services/CrashDumpService.cs (AnalyzeWithDbgEng) is where the new pass would slot in.
What
When
winapp run --debug-outputcaptures a crash dump from a WinUI app, the resulting stack is fairly thin — typically the user's faulting frame plus a couple of cswinrt ABI shims. There's no visibility into the XAML dispatcher / CoreMessaging / runtime chain that actually led to the failure, and no breakdown of stowed exceptions (0xC000027B) which are the most common WinUI crash shape.Why
Most WinUI crashes originate inside a XAML event handler and surface as a stowed exception wrapping a managed exception. The minimum context a developer needs to diagnose these is:
Today only the last one is reliably produced.
Potential approach
The WinUI team ships a WinDbg JavaScript extension (
winui-dbgext.jsinmicrosoft/microsoft-ui-xaml) that already produces exactly this output via!xamltriageand!xamlstowed. It runs against a minidump and works well with public symbols.A viable integration shape that avoids requiring a WinDbg install:
Microsoft.Debugging.Platform.DbgEngNuGet package (winapp already usesMicrosoft.Diagnostics.Runtime.Utilitiesfor its native fallback, so the API surface is familiar).Microsoft.Debugging.Platform.SymSrvpackage and co-locatesymsrv.dllnext todbgeng.dll— without this,srv*paths silently no-op and symbols never download. This is the main gotcha.Microsoft.Debugging.Platform.DbgXfordbgmodel.dll/msdia140.dll.JsProvider.dll(the JS scripting host for.scriptload) — this is not distributed on NuGet; it ships only with Debugging Tools for Windows. Options: bundle it, or download a WinDbg/SDK package on first use.winui-dbgext.jsfrom the public WinUI repo (pin a SHA)..scriptload winui-dbgext.js; !xamlstowed; !xamltriageagainst the same dump and append the output to the debug log.Gating it behind a flag (or auto-enabling when
Microsoft.UI.Xaml.dllis in the module list) keeps it scoped to WinUI apps.Notes for whoever picks this up
!xamlstowedproduces the most valuable output and works fully with the NuGet + JsProvider setup above.!xamltriage's managed-frame walker depends on SOS / CLR data access being loaded in the session; without that it logsCORDBG_E_MISSING_METADATAfor those frames. The headline summary (error code, message, user frames, ErrorContext) still comes through. Easiest path is probably to combine winapp's existing ClrMD managed stack with the extension's native output.src/winapp-CLI/WinApp.Cli/Services/CrashDumpService.cs(AnalyzeWithDbgEng) is where the new pass would slot in.