HandyOrg/HandyControl · error · Win32Exception
Win32Exception
Error message
Win32Exception
What it means
InteropMethods.GetDC is the shared P/Invoke wrapper around user32 GetDC. It calls IntGetDC(hWnd) and throws Win32Exception (with Marshal.GetLastWin32Error() as the error code) whenever the native call returns a null HDC. A successful HDC is registered with HandleCollector under CommonHandles.HDC so it gets released later.
Solutions
- Check the exception's ErrorCode/NativeErrorCode to identify the exact Win32 failure (e.g. ERROR_NO_SYSTEM_RESOURCES, ERROR_INVALID_WINDOW_HANDLE).
- Fix GDI handle leaks in the application (pair every GetDC with ReleaseDC; watch the GDI Objects counter in Task Manager).
- Only call DC-dependent APIs from a thread in an interactive desktop session.
- Wrap calls in try-catch (Win32Exception) and degrade gracefully (assume 96 DPI or skip rendering work).
- If triggered by a destroyed window handle, ensure the HWND is still alive (IsWindow) before requesting its DC.
Example fix
// before
var dc = InteropMethods.GetDC(new HandleRef(null, hwnd)); // throws Win32Exception
// after
IntPtr dc;
try { dc = InteropMethods.GetDC(new HandleRef(null, hwnd)); }
catch (Win32Exception ex)
{
Log.Warn($"GetDC failed: {ex.NativeErrorCode}");
return; // degrade gracefully
} Defensive patterns
Strategy: try-catch
Validate before calling
// check GDI headroom and window validity where possible bool canUse = Environment.UserInteractive; // and verify hwnd via IsWindow when not IntPtr.Zero
Type guard
static bool IsValidHwnd(IntPtr hwnd) => hwnd == IntPtr.Zero || InteropMethods.IsWindow(hwnd);
Try / catch
try { hdc = InteropMethods.GetDC(new HandleRef(null, hwnd)); }
catch (Win32Exception ex) { Log.Warn($"GetDC failed: {ex.NativeErrorCode}"); hdc = IntPtr.Zero; /* skip DC work */ } Prevention
- Pair every GetDC with a ReleaseDC; audit for GDI leaks.
- Verify window handles are alive (IsWindow) before requesting DCs.
- Only acquire screen DCs from interactive desktop sessions.
- Log ex.NativeErrorCode to distinguish quota vs invalid-handle causes.
When it happens
Trigger: Any HandyControl code path that acquires a device context — e.g. WindowHelper DPI detection, window rendering helpers — when GetDC fails: invalid window HandleRef, GDI object quota exhausted, or no desktop available for the calling session.
Common situations: Session 0 / headless execution, disconnected terminal sessions, GDI handle leaks reaching the 10,000-per-process default quota, passing a destroyed window's handle.
Related errors
- Win32Exception
- Unable to create a device context from the specified device…
- Unable to combine two HRGNs.
- Unable to initialize GDI+
- StatusException(status)
AI-assisted analysis of HandyOrg/HandyControl@2c0875ebd6 (2026-09-14).
Data as JSON: /api/errors/3244191014e1ed27.
Report an issue: GitHub.
Appendix: source
Thrown at src/Shared/HandyControl_Shared/Tools/Interop/InteropMethods.cs:114
[DllImport(InteropValues.ExternDll.User32)]
internal static extern bool InsertMenu(IntPtr hMenu, int wPosition, int wFlags, int wIDNewItem, string lpNewItem);
[DllImport(InteropValues.ExternDll.User32, ExactSpelling = true, EntryPoint = "DestroyMenu", CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern bool IntDestroyMenu(HandleRef hMenu);
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(InteropValues.ExternDll.User32, SetLastError = true, ExactSpelling = true, EntryPoint = nameof(GetDC),
CharSet = CharSet.Auto)]
internal static extern IntPtr IntGetDC(HandleRef hWnd);
[SecurityCritical]
internal static IntPtr GetDC(HandleRef hWnd)
{
var hDc = IntGetDC(hWnd);
if (hDc == IntPtr.Zero) throw new Win32Exception();
return HandleCollector.Add(hDc, CommonHandles.HDC);
}
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]
[DllImport(InteropValues.ExternDll.User32, ExactSpelling = true, EntryPoint = nameof(ReleaseDC), CharSet = CharSet.Auto)]
internal static extern int IntReleaseDC(HandleRef hWnd, HandleRef hDC);
[SecurityCritical]
internal static int ReleaseDC(HandleRef hWnd, HandleRef hDC)
{
HandleCollector.Remove((IntPtr) hDC, CommonHandles.HDC);
return IntReleaseDC(hWnd, hDC);
}
[SecurityCritical]
[SuppressUnmanagedCodeSecurity]View on GitHub (pinned to 2c0875ebd6)