lepoco/wpfui · error · InvalidOperationException
Could not get window handle.
Error message
Could not get window handle.
What it means
Thrown in ObserveLoadedWindow when new WindowInteropHelper(window).Handle returns IntPtr.Zero, meaning the WPF window has no HWND yet. This path is for windows expected to already be loaded, so a zero handle indicates the caller invoked Observe too early or on an uninitialized window.
Source
Thrown at src/Wpf.Ui/Appearance/SystemThemeWatcher.cs:72
{
ObserveWindowWhenLoaded(window, backdrop, updateAccents);
}
if (_observedWindows.Count == 0)
{
System.Diagnostics.Debug.WriteLine(
$"INFO | {typeof(SystemThemeWatcher)} changed the app theme on initialization.",
nameof(SystemThemeWatcher)
);
ApplicationThemeManager.ApplySystemTheme(updateAccents);
}
}
private static void ObserveLoadedWindow(Window window, WindowBackdropType backdrop, bool updateAccents)
{
IntPtr hWnd =
(hWnd = new WindowInteropHelper(window).Handle) == IntPtr.Zero
? throw new InvalidOperationException("Could not get window handle.")
: hWnd;
if (hWnd == IntPtr.Zero)
{
throw new InvalidOperationException("Window handle cannot be empty");
}
ObserveLoadedHandle(new ObservedWindow(hWnd, backdrop, updateAccents));
}
private static void ObserveWindowWhenLoaded(
Window window,
WindowBackdropType backdrop,
bool updateAccents
)
{
window.Loaded += (_, _) =>
{View on GitHub (pinned to ffebacd610)
Solutions
- Call Observe from the Window.Loaded event (or use ObserveWindowWhenLoaded) so the HWND exists.
- Defer watching until IsLoaded is true: SystemThemeWatcher.Watch(window) which hooks Loaded internally when needed.
- Ensure the window is actually shown (Show/ShowDialog) before observing it.
Example fix
// before SystemThemeWatcher.Observe(window); // in constructor, handle is 0 // after window.Loaded += (_, _) => SystemThemeWatcher.Observe(window);
Defensive patterns
Strategy: validation
Validate before calling
if (new WindowInteropHelper(window).Handle == IntPtr.Zero) { /* defer to Loaded/SourceInitialized */ } Type guard
static bool WindowHasHandle(System.Windows.Window w) => w.IsLoaded && new WindowInteropHelper(w).Handle != IntPtr.Zero;
Prevention
- Observe windows only after Loaded or SourceInitialized.
- Use SystemThemeWatcher.Watch which hooks Loaded internally for not-yet-loaded windows.
- Ensure the window is shown (Show/ShowDialog) before observing.
When it happens
Trigger: Calling SystemThemeWatcher.Observe (the synchronous path) on a Window that has not completed its first render pass, so WindowInteropHelper.Handle is still zero. The inline ternary expression throws as soon as the handle compares equal to IntPtr.Zero.
Common situations: Watching a window in its constructor, in Window.Initialized (before HWND creation), or a window whose Show has not been called. Also when the visual tree is not yet hooked to a presentation source.
Related errors
- Unable to determine the window source.
- You cannot unwatch a window that is not yet loaded.
- Window handle cannot be empty
- Failed to get the current `_desiredWidth`.
- Use {nameof(Close)} with MessageBoxResult instead
AI-assisted analysis of lepoco/wpfui@ffebacd610 (2026-08-13).
Data as JSON: /api/errors/113e980b93afbe3c.
Report an issue: GitHub.