microsoft / microsoft/microsoft-ui-xaml
TitleBar reading AppWindow.Title during layout can fail-fast the process (windowing GetTitle E_INVALIDARG on empty title)
- Dominant language
- C++
- Stars
- 8.4k
- Forks
- 942
- Avg merge
- 2d 7h
- Merged PRs (30d)
- 105
Description
### Description
An app that uses the `TitleBar` control (with `ExtendsContentIntoTitleBar`) can **fail-fast during startup** with `E_INVALIDARG (0x80070057)` originating from the windowing layer while resolving `AppWindow.Title`, triggered from `TitleBar::UpdateTitle` during a deferred layout pass.
Representative stack (top frames):
```
Microsoft.UI.Windowing!...RaiseFailFastException
Microsoft.UI.Windowing!CFlat::Abandonment::FailWithHR
Microsoft.UI.Windowing!...Marshal::ThrowExceptionForHR
Microsoft.UI.Windowing!Core::YieldAndCall::GetTitle // AppWindow.Title getter
Microsoft.UI.Windowing!Api::IAppWindow::get_Title
Microsoft.UI.Xaml.Controls!TitleBar::UpdateTitle
Microsoft.UI.Xaml.Controls!TitleBar::OnApplyTemplate
...deferred layout / measure pass...
```
### Root cause
`AppWindow.Title`'s getter is a wrapper over `GetWindowTextLengthW` / `GetWindowTextW`. `GetWindowTextLengthW` returns **0 for a window with an empty title**, which is a *valid* result — not an error — and (per Win32 docs) it does **not** reset the thread's last-error on that path.
The getter treats length `0` as failure and converts whatever value `GetLastError()` currently holds. When a previous, unrelated call on the UI thread left `ERROR_INVALID_PARAMETER (0x57)` behind, the getter produces `HRESULT_FROM_WIN32(0x57) = 0x80070057 (E_INVALIDARG)`, and the windowing layer escalates that failed HRESULT to a **fail-fast**.
This is intermittent by nature: it requires the native window title to be **empty** at the instant of the read **and** a stale `0x57` on the thread at the same time. `TitleBar::UpdateTitle` reads `AppWindow.Title` during a deferred layout pass (`OnApplyTemplate` → `UpdateTitle`, and again from `ResetTitle`), which is exactly when a window's native title can still be momentarily empty during startup.
### Where it comes from in TitleBar
`TitleBar::UpdateTitle` reads the window title twice:
```cpp
// capture default (fires on first UpdateTitle, e.g. during OnApplyTemplate)
m_defaultAppWindowTitle = appWindow.Title();
...
// redundant-set optimization
const auto currentTitle = appWindow.Title();
if (currentTitle != titleText) { appWindow.Title(titleText); }
```
and `TitleBar::ResetTitle` reads it a third time. Each of these getter reads can trip the windowing fail-fast above.
### Suggested fixes
Two independent layers:
1. **Windowing (the true fix):** `AppWindow.Title`'s getter should treat a length of `0` as a valid empty title. Concretely: `SetLastError(ERROR_SUCCESS)` before `GetWindowTextLengthW`/`GetWindowTextW`, and only treat a *nonzero* last-error as a real failure (return an empty string with `S_OK` otherwise). This is the classic `GetWindowTextLength`-returns-0-for-empty-title pattern documented on MSDN.
2. **WinUI `TitleBar` (mitigation / hardening):** avoid reading `AppWindow.Title()` back in `UpdateTitle`/`ResetTitle`. A `try/catch` in the control does **not** help, because the failure is a fail-fast *inside* the windowing callee, not a returnable HRESULT — so the only WinUI-side mitigation is to not perform the getter reads (set the title unconditionally; track state internally rather than reading back). Tradeoff: the automatic "restore the window's original title when `TitleBar.Title` is cleared" behavior would need to be reworked or dropped. I'll open a draft PR with this approach for discussion.
### Workaround (app side)
Ensure the window's native title is never empty when the `TitleBar` is present (e.g., set a non-empty `Window.Title`/`AppWindow.Title` before the title bar's first layout, and guard against empty title strings from resource lookups). Example: microsoft/PowerToys#49069.
### Version
- Windows App SDK 2.0.x (`TitleBar` control), reproduced on in-market Windows 11.
Contributor guide
Research direction
Start at TitleBar::UpdateTitle and ResetTitle, including the path from OnApplyTemplate, and trace each AppWindow.Title read into the windowing getter. Compare the two suggested fix layers and determine which code is available in this repository. Done means an empty native title no longer causes startup fail-fast while TitleBar title behavior remains correct.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- desktop, operating-systems
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100