dotnet/wpf · error · Win32Exception

Win32Exception

Error message

Win32Exception

What it means

During HwndWrapper construction the class registers a window class, first fetching the NULL_BRUSH stock object via CriticalGetStockObject. If that Win32 call returns a null handle, the wrapper throws a Win32Exception carrying the last Win32 error, since it cannot proceed to register the class without a valid brush handle.

Solutions

  1. Check GDI handle usage (Task Manager > GDI Objects) and fix leaks of brushes/bitmaps/DCs in the process
  2. Restart the process to release exhausted GDI handles
  3. Inspect Marshal.GetLastWin32Error in the exception for the specific OS error code
  4. Reduce GDI resource consumption (dispose HBRUSH/HBITMAP/HDC deterministically)
Defensive patterns

Strategy: try-catch

Try / catch

try { var wrapper = new MyHwndWrapper(...); } catch (Win32Exception ex) { log(ex.NativeErrorCode); /* surface GDI-exhaustion to user, suggest restart */ }

Prevention

When it happens

Trigger: CriticalGetStockObject(NULL_BRUSH) returning IntPtr.Zero during HwndWrapper's constructor — an OS-level GDI failure such as GDI handle exhaustion or an uninitialized/invalid GDI environment.

Common situations: Processes that leaked thousands of GDI handles hitting the per-process 10,000 GDI object limit; running in stripped-down/remote/session environments where stock objects are unavailable.

Related errors


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

Appendix: source

Thrown at src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/HwndWrapper.cs:61

                }
            }


            _wndProc = new HwndWrapperHook(WndProc);

            // We create the HwndSubclass object so that we can use its
            // window proc directly.  We will not be "subclassing" the
            // window we create.
            HwndSubclass hwndSubclass = new(_wndProc);
            
            // Register a unique window class for this instance.
            NativeMethods.WNDCLASSEX_D wc_d = new NativeMethods.WNDCLASSEX_D();

            IntPtr hNullBrush = UnsafeNativeMethods.CriticalGetStockObject(NativeMethods.NULL_BRUSH);

            if (hNullBrush == IntPtr.Zero)
            {
                throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
            }

            IntPtr hInstance = UnsafeNativeMethods.GetModuleHandle( null );

            // We need to keep the Delegate object alive through the call to CreateWindowEx().
            // Subclass.WndProc will install a better delegate (to the same function) when it
            // processes the first message.
            // But this first delegate needs be held alive until then.
            NativeMethods.WndProc initialWndProc = new NativeMethods.WndProc(hwndSubclass.SubclassWndProc);

            // The class name is a concat of AppName, ThreadName, and RandomNumber.
            // Register will fail if the string gets over 255 in length.
            // So limit each part to a reasonable amount.
            string appName;
            string currentDomainFriendlyName = AppDomain.CurrentDomain.FriendlyName;
            if (null != currentDomainFriendlyName && 128 <= currentDomainFriendlyName.Length)
                appName = currentDomainFriendlyName[..128];
            else

View on GitHub (pinned to 81131a70a4)