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

  1. Fix the GDI handle leak (e.g. always ReleaseDC / dispose Graphics objects) and verify GDI object count in Task Manager
  2. Do not create WPF UI in a non-interactive session (Session 0); run as an interactive user or use session-0-safe alternatives
  3. Retry after freeing system resources / rebooting a degraded session
  4. 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

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)