Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .claude/agents/winapp.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,8 @@ Want to inspect or interact with a running app's UI?
- `--args <string>` — command-line arguments to pass to the app
- `--no-launch` — register the package without launching
- `--with-alias` — launch via execution alias (console apps run in current terminal)
- `--debug-output` — capture `OutputDebugString` messages and first-chance exceptions (prevents other debuggers like VS/VS Code from attaching)
- `--debug-output` — capture `OutputDebugString` messages and first-chance exceptions (prevents other debuggers like VS/VS Code from attaching). For WinUI apps it also auto-runs a stowed-exception (`0xC000027B`) triage pass (`!xamlstowed`/`!xamltriage`) that recovers the originating HRESULT and native XAML dispatch stack. The first triage run downloads debugger components (engine bits from NuGet + `JsProvider.dll` from the WinDbg CDN) and caches them under `~\.winapp\dbgtools\`; if downloads are blocked, install Debugging Tools for Windows or point `WINAPP_DBGTOOLS_DIR` at a debugger directory containing `dbgeng.dll` and `JsProvider.dll`.
- `--symbols` — with `--debug-output`, download Microsoft public symbols for richer native crash stacks (first run downloads and caches them)
- `--output-appx-directory <path>` — custom output directory for loose layout
**Requires:** Built app output directory + `appxmanifest.xml`

Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/winapp-setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Use `winapp run` during iterative development — it creates a loose layout pack

For console apps, add `--with-alias` to preserve stdin/stdout in the current terminal.

> **`--debug-output` caveat:** Captures `OutputDebugString` and crash diagnostics (minidump + automatic analysis for both managed and native crashes) but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. Add `--symbols` to download PDB symbols for richer native crash analysis.
> **`--debug-output` caveat:** Captures `OutputDebugString` and crash diagnostics (minidump + automatic analysis for both managed and native crashes) but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. Add `--symbols` to download PDB symbols for richer native crash analysis. For WinUI 3 apps, a stowed-exception triage pass runs automatically (surfacing the originating HRESULT and native XAML dispatch stack); the debugger components it needs are downloaded on first use, or set `WINAPP_DBGTOOLS_DIR` to a directory containing `dbgeng.dll` and `JsProvider.dll` for offline/locked-down environments.

For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md).

Expand Down Expand Up @@ -228,13 +228,13 @@ Creates packaged layout, registers the Application, and launches the packaged ap
|--------|-------------|---------|
| `--args` | Command-line arguments to pass to the application. Alternatively, use -- followed by arguments to avoid escaping (e.g., winapp run . -- --flag value). | (none) |
| `--clean` | Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. | (none) |
| `--debug-output` | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | (none) |
| `--debug-output` | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. For WinUI apps, a crash also triggers a stowed-exception triage pass; the first run downloads debugger components (cached under the winapp global directory) and can be pointed at an existing debugger install via the WINAPP_DBGTOOLS_DIR environment variable. Cannot be combined with --no-launch or --json. | (none) |
| `--detach` | Launch the application and return immediately without waiting for it to exit. Useful for CI/automation where you need to interact with the app after launch. Prints the PID to stdout (or in JSON with --json). | (none) |
| `--executable` | Path to the executable relative to the input folder. Use to disambiguate when the manifest contains a $targetnametoken$ placeholder and multiple .exe files are present in the input folder. | (none) |
| `--json` | Format output as JSON | (none) |
| `--manifest` | Path to the Package.appxmanifest (default: auto-detect from input folder or current directory) | (none) |
| `--no-launch` | Only create the debug identity and register the package without launching the application | (none) |
| `--output-appx-directory` | Output directory for the loose layout package. If not specified, a directory named AppX inside the input-folder directory will be used. | (none) |
| `--symbols` | Download symbols from Microsoft Symbol Server for richer native crash analysis. Only used with --debug-output. First run downloads symbols and caches them locally; subsequent runs use the cache. | (none) |
| `--symbols` | Download symbols from Microsoft Symbol Server for richer native crash analysis, including the WinUI stowed-exception dispatch stack. Only used with --debug-output. First run downloads symbols and caches them locally; subsequent runs use the cache. | (none) |
| `--unregister-on-exit` | Unregister the development package after the application exits. Only removes packages registered in development mode. | (none) |
| `--with-alias` | Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a uap5:ExecutionAlias in the manifest. Use "winapp manifest add-alias" to add an execution alias to the manifest. | (none) |
3 changes: 2 additions & 1 deletion .github/plugin/agents/winapp.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,8 @@ Want to inspect or interact with a running app's UI?
- `--args <string>` — command-line arguments to pass to the app
- `--no-launch` — register the package without launching
- `--with-alias` — launch via execution alias (console apps run in current terminal)
- `--debug-output` — capture `OutputDebugString` messages and first-chance exceptions (prevents other debuggers like VS/VS Code from attaching)
- `--debug-output` — capture `OutputDebugString` messages and first-chance exceptions (prevents other debuggers like VS/VS Code from attaching). For WinUI apps it also auto-runs a stowed-exception (`0xC000027B`) triage pass (`!xamlstowed`/`!xamltriage`) that recovers the originating HRESULT and native XAML dispatch stack. The first triage run downloads debugger components (engine bits from NuGet + `JsProvider.dll` from the WinDbg CDN) and caches them under `~\.winapp\dbgtools\`; if downloads are blocked, install Debugging Tools for Windows or point `WINAPP_DBGTOOLS_DIR` at a debugger directory containing `dbgeng.dll` and `JsProvider.dll`.
- `--symbols` — with `--debug-output`, download Microsoft public symbols for richer native crash stacks (first run downloads and caches them)
- `--output-appx-directory <path>` — custom output directory for loose layout
**Requires:** Built app output directory + `appxmanifest.xml`

Expand Down
6 changes: 3 additions & 3 deletions .github/plugin/skills/winapp-cli/setup/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ Use `winapp run` during iterative development — it creates a loose layout pack

For console apps, add `--with-alias` to preserve stdin/stdout in the current terminal.

> **`--debug-output` caveat:** Captures `OutputDebugString` and crash diagnostics (minidump + automatic analysis for both managed and native crashes) but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. Add `--symbols` to download PDB symbols for richer native crash analysis.
> **`--debug-output` caveat:** Captures `OutputDebugString` and crash diagnostics (minidump + automatic analysis for both managed and native crashes) but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. Add `--symbols` to download PDB symbols for richer native crash analysis. For WinUI 3 apps, a stowed-exception triage pass runs automatically (surfacing the originating HRESULT and native XAML dispatch stack); the debugger components it needs are downloaded on first use, or set `WINAPP_DBGTOOLS_DIR` to a directory containing `dbgeng.dll` and `JsProvider.dll` for offline/locked-down environments.

For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md).

Expand Down Expand Up @@ -228,13 +228,13 @@ Creates packaged layout, registers the Application, and launches the packaged ap
|--------|-------------|---------|
| `--args` | Command-line arguments to pass to the application. Alternatively, use -- followed by arguments to avoid escaping (e.g., winapp run . -- --flag value). | (none) |
| `--clean` | Remove the existing package's application data (LocalState, settings, etc.) before re-deploying. By default, application data is preserved across re-deployments. | (none) |
| `--debug-output` | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json. | (none) |
| `--debug-output` | Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. For WinUI apps, a crash also triggers a stowed-exception triage pass; the first run downloads debugger components (cached under the winapp global directory) and can be pointed at an existing debugger install via the WINAPP_DBGTOOLS_DIR environment variable. Cannot be combined with --no-launch or --json. | (none) |
| `--detach` | Launch the application and return immediately without waiting for it to exit. Useful for CI/automation where you need to interact with the app after launch. Prints the PID to stdout (or in JSON with --json). | (none) |
| `--executable` | Path to the executable relative to the input folder. Use to disambiguate when the manifest contains a $targetnametoken$ placeholder and multiple .exe files are present in the input folder. | (none) |
| `--json` | Format output as JSON | (none) |
| `--manifest` | Path to the Package.appxmanifest (default: auto-detect from input folder or current directory) | (none) |
| `--no-launch` | Only create the debug identity and register the package without launching the application | (none) |
| `--output-appx-directory` | Output directory for the loose layout package. If not specified, a directory named AppX inside the input-folder directory will be used. | (none) |
| `--symbols` | Download symbols from Microsoft Symbol Server for richer native crash analysis. Only used with --debug-output. First run downloads symbols and caches them locally; subsequent runs use the cache. | (none) |
| `--symbols` | Download symbols from Microsoft Symbol Server for richer native crash analysis, including the WinUI stowed-exception dispatch stack. Only used with --debug-output. First run downloads symbols and caches them locally; subsequent runs use the cache. | (none) |
| `--unregister-on-exit` | Unregister the development package after the application exits. Only removes packages registered in development mode. | (none) |
| `--with-alias` | Launch the app using its execution alias instead of AUMID activation. The app runs in the current terminal with inherited stdin/stdout/stderr. Requires a uap5:ExecutionAlias in the manifest. Use "winapp manifest add-alias" to add an execution alias to the manifest. | (none) |
4 changes: 2 additions & 2 deletions docs/cli-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1379,7 +1379,7 @@
"recursive": false
},
"--debug-output": {
"description": "Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. Cannot be combined with --no-launch or --json.",
"description": "Capture OutputDebugString messages and first-chance exceptions from the launched application. Only one debugger can attach to a process at a time, so other debuggers (Visual Studio, VS Code) cannot be used simultaneously. Use --no-launch instead if you need to attach a different debugger. For WinUI apps, a crash also triggers a stowed-exception triage pass; the first run downloads debugger components (cached under the winapp global directory) and can be pointed at an existing debugger install via the WINAPP_DBGTOOLS_DIR environment variable. Cannot be combined with --no-launch or --json.",
"hidden": false,
"valueType": "System.Boolean",
"hasDefaultValue": true,
Expand Down Expand Up @@ -1486,7 +1486,7 @@
"recursive": false
},
"--symbols": {
"description": "Download symbols from Microsoft Symbol Server for richer native crash analysis. Only used with --debug-output. First run downloads symbols and caches them locally; subsequent runs use the cache.",
"description": "Download symbols from Microsoft Symbol Server for richer native crash analysis, including the WinUI stowed-exception dispatch stack. Only used with --debug-output. First run downloads symbols and caches them locally; subsequent runs use the cache.",
"hidden": false,
"valueType": "System.Boolean",
"hasDefaultValue": true,
Expand Down
10 changes: 10 additions & 0 deletions docs/debugging.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,16 @@ winapp run .\build\Debug --debug-output --symbols

> **Important:** This attaches winapp as the debugger. Windows only allows one debugger per process, so you **cannot** also attach Visual Studio, VS Code, or WinDbg.

#### WinUI stowed-exception triage

Most WinUI crashes start inside a XAML event handler and surface as a **stowed exception** (`0xC000027B`) that is re-raised later from the dispatcher, so the normal stack no longer points at the real cause. When the crashed app loaded `Microsoft.UI.Xaml.dll`, winapp automatically runs an extra triage pass that decodes the stowed exception and the native XAML dispatch chain (`Microsoft.UI.Xaml` → `CXcpDispatcher` → `CoreMessagingXP` → CLR host). The result is appended to the debug log. No flag is needed — it is enabled automatically for WinUI dumps. Add `--symbols` for fully resolved function names in the dispatch chain.

To make this work, winapp captures the crash dump with the terminating stowed exception's record (and its parameters, which point at the stowed-exception array) while keeping the first-chance thread context, so the standard managed analysis still recovers your original user frame *and* the triage pass can locate the stowed exception.

This pass hosts DbgEng with the WinUI team's WinDbg JavaScript extension. The native debugging engine (`dbgeng.dll` and friends) comes from NuGet, and `JsProvider.dll` — the JavaScript scripting host, which is **not** on NuGet — is fetched on first use directly from the official WinDbg download (only the few hundred kilobytes needed are read, not the full package). Because `JsProvider.dll` must be the same build as the engine — loading a mismatched provider crashes the debugger on startup — it is pinned to the specific WinDbg bundle whose build matches the NuGet engine (not the rolling "current" release), and after acquisition the two builds are compared: a mismatch is rejected and triage is skipped with a clear reason rather than crashing silently. The debugger packages are version-pinned and, before any of their native DLLs are extracted and loaded, verified against a compiled-in SHA-512 content hash (and the extension against a pinned hash); `JsProvider.dll` is additionally required to carry a valid Microsoft Authenticode signature, checked with full certificate-chain revocation (falling back to a signature-only check when revocation data can't be reached offline, but always rejecting a revoked certificate). Downloads are staged to a temporary file, verified, then atomically published into the cache, so a concurrent run never observes a partially-written or unverified DLL. On every run the cached binaries are re-checked (the `JsProvider.dll` signature and its engine-build match, and the engine DLLs for a valid PE image), so a truncated or drifted cache self-heals by re-acquiring instead of failing every run. Together this means a mirrored or compromised feed cannot substitute altered binaries — any failure skips triage rather than loading unverified code. Everything is cached under the winapp global directory, so subsequent runs are offline. If your environment blocks those downloads, install **Debugging Tools for Windows** (via the Windows SDK) or set the `WINAPP_DBGTOOLS_DIR` environment variable to a debugger directory that already contains `dbgeng.dll` and `JsProvider.dll`. When `WINAPP_DBGTOOLS_DIR` is set it is authoritative — only that directory is consulted — so if it's incomplete the log names the specific missing component (`dbgeng.dll` and/or `JsProvider.dll`) rather than suggesting you set a variable that's already set. When triage succeeds, the console surfaces a one-line verdict (the stowed exception's error code/message); when the binaries can't be obtained, the triage pass is skipped (the standard managed/native analysis still runs), the console says so, and the log explains why.

The triage pass runs in a short-lived child process. This is required: winapp's main process loads the system `dbghelp.dll` while capturing and analyzing the dump, and the modern engine `dbgeng.dll` cannot bind to that older, already-resident copy — a fresh process gives the engine a clean loader state. Decoding the stowed-exception structures also needs operating-system symbols (`combase.dll`), which `--symbols` downloads from the Microsoft public symbol server; on builds whose symbols aren't published there, the triage pass still identifies the stowed exception but cannot fully expand it.

## IDE setup

### VS Code
Expand Down
2 changes: 1 addition & 1 deletion docs/fragments/skills/winapp-cli/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ Use `winapp run` during iterative development — it creates a loose layout pack

For console apps, add `--with-alias` to preserve stdin/stdout in the current terminal.

> **`--debug-output` caveat:** Captures `OutputDebugString` and crash diagnostics (minidump + automatic analysis for both managed and native crashes) but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. Add `--symbols` to download PDB symbols for richer native crash analysis.
> **`--debug-output` caveat:** Captures `OutputDebugString` and crash diagnostics (minidump + automatic analysis for both managed and native crashes) but attaches winapp as the debugger — you cannot also attach VS Code or WinDbg. Use `--no-launch` if you need your own debugger. Add `--symbols` to download PDB symbols for richer native crash analysis. For WinUI 3 apps, a stowed-exception triage pass runs automatically (surfacing the originating HRESULT and native XAML dispatch stack); the debugger components it needs are downloaded on first use, or set `WINAPP_DBGTOOLS_DIR` to a directory containing `dbgeng.dll` and `JsProvider.dll` for offline/locked-down environments.

For full debugging scenarios and IDE setup, see the [Debugging Guide](https://github.com/microsoft/WinAppCli/blob/main/docs/debugging.md).

Expand Down
Loading
Loading