dotnet/wpf · error · Win32Exception
Win32Exception (unmanaged GetDC returned zero; Win32 error…
Error message
Win32Exception (unmanaged GetDC returned zero; Win32 error code carried by Win32Exception)
What it means
Win32's GetDC returned a null device-context handle while UIElement.EnsureDpiScale queried the desktop DC for LOGPIXELSX/LOGPIXELSY. The library cannot determine system DPI, so it surfaces the raw Win32 error via Win32Exception. This indicates the Win32/GDI environment could not provide a DC for the desktop window.
Solutions
- Fix the GDI handle leak (e.g. always ReleaseDC / dispose Graphics objects) and verify GDI object count in Task Manager
- Do not create WPF UI in a non-interactive session (Session 0); run as an interactive user or use session-0-safe alternatives
- Retry after freeing system resources / rebooting a degraded session
- Capture Win32Exception.ErrorCode/NativeErrorCode to identify the exact Win32 failure
Example fix
// before
IntPtr dc = UnsafeNativeMethods.GetDC(desktopWnd);
try { ... } finally { UnsafeNativeMethods.ReleaseDC(desktopWnd, dc); }
// after
IntPtr dc = UnsafeNativeMethods.GetDC(desktopWnd);
if (dc == IntPtr.Zero) { log.Error($"GetDC failed: {Kernel32.GetLastError()}"); return; }
try { ... } finally { UnsafeNativeMethods.ReleaseDC(desktopWnd, dc); } Defensive patterns
Strategy: try-catch
Validate before calling
// Check GDI headroom and interactive session before UI work bool canQueryDpi = Environment.UserInteractive && GetGdiHandlesInUse(Process.GetCurrentProcess()) < 9000;
Try / catch
try { element.UpdateLayout(); }
catch (Win32Exception ex) { log.Error($"Win32 error {ex.NativeErrorCode} during DPI detection", ex); /* degrade gracefully */ } Prevention
- Always pair GetDC with ReleaseDC and dispose GDI wrappers (Graphics, Pen, Brush)
- Monitor the process GDI object count (Task Manager > Details > GDI Objects)
- Never build WPF visual trees inside Session 0 / Windows services
When it happens
Trigger: Calling WPF APIs that trigger DPI detection (element measure/arrange, window creation, DPI-changed handling) when GetDC(desktopWnd) returns IntPtr.Zero, e.g. in a non-interactive session or after GDI handle exhaustion.
Common situations: Running a WPF app in a Windows Service (Session 0) with no interactive desktop; leaking GDI handles until the process hits the 10,000 GDI-object cap; terminal-service/headless environments; heavily loaded systems low on system resources.
Related errors
AI-assisted analysis of dotnet/wpf@81131a70a4 (2026-09-14).
Data as JSON: /api/errors/aa72425b1ec287c2.
Report an issue: GitHub.
Appendix: source
Thrown at src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/UIElement.cs:1142
/// </summary>
/// <remarks>
/// Should be called before reading _dpiScaleX and _dpiScaleY
/// </remarks>
internal static DpiScale EnsureDpiScale()
{
if (_setDpi)
{
_setDpi = false;
int dpiX, dpiY;
HandleRef desktopWnd = new HandleRef(null, IntPtr.Zero);
// Win32Exception will get the Win32 error code so we don't have to
IntPtr dc = UnsafeNativeMethods.GetDC(desktopWnd);
// Detecting error case from unmanaged call, required by PREsharp to throw a Win32Exception
if (dc == IntPtr.Zero)
{
throw new Win32Exception();
}
try
{
dpiX = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSX);
dpiY = UnsafeNativeMethods.GetDeviceCaps(new HandleRef(null, dc), NativeMethods.LOGPIXELSY);
_dpiScaleX = (double)dpiX / DpiUtil.DefaultPixelsPerInch;
_dpiScaleY = (double)dpiY / DpiUtil.DefaultPixelsPerInch;
}
finally
{
UnsafeNativeMethods.ReleaseDC(desktopWnd, new HandleRef(null, dc));
}
}
return new DpiScale(_dpiScaleX, _dpiScaleY);
}
/// <summary>View on GitHub (pinned to 81131a70a4)