dotnet/wpf · error · Win32Exception

new System.ComponentModel.Win32Exception(win32Error)

Error message

new System.ComponentModel.Win32Exception(win32Error)

What it means

CaretElement.Win32CreateCaret throws Win32Exception when the native CreateCaret call fails, wrapping Marshal.GetLastWin32Error(). The Win32 caret (used for text-selection rendering interop) could not be created for the target HWND, typically because the window handle is invalid, the window is being destroyed, or another thread owns the caret.

Solutions

  1. Ensure the caret is only created/positioned on the UI thread owning the HWND (Dispatcher.Invoke if needed).
  2. Delay or skip caret creation when the window/HwndSource handle is invalid or the window is closing.
  3. Catch Win32Exception around caret operations and retry on the next blink/render cycle rather than crashing.
  4. Check window handle lifetime (HwndSource.IsDisposed, IntPtr.Zero checks) before caret operations.

Example fix

// before
// inside caret update: throws Win32Exception when HWND invalid
win32CreateCaret(...);
// after
try {
    win32CreateCaret(...);
} catch (System.ComponentModel.Win32Exception) {
    // handle gone or thread mismatch; retry on next layout/blink cycle
    Dispatcher.BeginInvoke(DispatcherPriority.Render, new Action(UpdateCaret));
}
Defensive patterns

Strategy: retry

Validate before calling

var source = HwndSource.FromVisual(textBox) as HwndSource;
bool canCreate = source != null && !source.IsDisposed && source.Handle != IntPtr.Zero;

Type guard

bool HasLiveHandle(HwndSource s) => s != null && !s.IsDisposed && s.Handle != IntPtr.Zero;

Try / catch

try { /* caret creation path */ } catch (Win32Exception ex) { Dispatcher.BeginInvoke(() => RetryCaretCreation()); /* log ex.ErrorCode */ }

Prevention

When it happens

Trigger: SetBlinking/Win32SetCaretPos triggering caret (re)creation against an HWND that is invalid, already destroyed, or belongs to another thread; a window being closed while the text editor still tries to show the caret; resource exhaustion preventing caret creation.

Common situations: Closing or hiding a window during a focus change while the caret blink timer fires; hosting WPF content in HwndSource with rapid handle recreation; multi-threaded UI access to the caret; terminal-service/RDP sessions where caret creation intermittently fails.

Related errors


AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14). Data as JSON: /api/errors/fd1a391cd6a4b601. Report an issue: GitHub.

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/PresentationFramework/System/Windows/Documents/CaretElement.cs:999

                    // Specified height with the current caret height will sync the win32 caret which
                    // Win32 Magnifer rely on the caret height to scroll their window.
                    NativeMethods.BitmapHandle bitmap = UnsafeNativeMethods.CreateBitmap(/*width*/ 1, /*height*/ ConvertToInt32(deviceHeight), /*panels*/ 1, /*bitsPerPixel*/ 1, /*bits*/ null);

                    // Specified width and height as zero since they will be ignored by setting the bitmap.
                    bool returnValue = UnsafeNativeMethods.CreateCaret(new HandleRef(null, hwnd), bitmap, /*width*/ 0, /*height*/ 0);

                    int win32Error = Marshal.GetLastWin32Error();
                    if (returnValue)
                    {
                        _win32Caret = true;
                        _win32Height = _height;
                    }
                    else
                    {
                        _win32Caret = false;

                        // Throw the Win32 exception with GetLastWin32Error.
                        throw new System.ComponentModel.Win32Exception(win32Error);
                    }
                }
            }
        }

        // Destroy Win32 caret if we create it with checking Win32 error.
        private void Win32DestroyCaret()
        {
            if (!_isSelectionActive)
            {
                // We do not want to interfere with Win32 caret
                // if this Adorner isnt representing active selection
                return;
            }

            // We only destroy the caret what we created a Win32 caret.
            if (_win32Caret)
            {

View on GitHub (pinned to 81131a70a4)