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
- Check GDI handle usage (Task Manager > GDI Objects) and fix leaks of brushes/bitmaps/DCs in the process
- Restart the process to release exhausted GDI handles
- Inspect Marshal.GetLastWin32Error in the exception for the specific OS error code
- 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
- Monitor GDI object count (10,000 per-process limit) and fix brush/DC/bitmap leaks
- Dispose GDI handles deterministically (DeleteObject/DeleteDC)
- Watch for this failure after long-running sessions with heavy graphics usage
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
- Win32Exception
- Win32Exception (unmanaged GetDC returned zero; Win32 error…
- ArgumentNullException
- InvalidOperationException
- InvalidOperationException
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];
elseView on GitHub (pinned to 81131a70a4)