{"record":{"id":"75902ce5eb7a909f","repo":"dotnet/wpf","slug":"win32exception-unsafenativemethodsclr","errorCode":null,"errorMessage":"Win32Exception","messagePattern":"Win32Exception","errorType":"exception","errorClass":"Win32Exception","httpStatus":null,"severity":"error","filePath":"src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs","lineNumber":101,"sourceCode":"        [DllImport(ExternDll.Gdi32, EntryPoint = \"GetStockObject\", SetLastError = true, CharSet = CharSet.Auto)]\n        public static extern IntPtr CriticalGetStockObject(int stockObject);\n\n        [DllImport(ExternDll.User32, EntryPoint = \"FillRect\", SetLastError = true, CharSet = CharSet.Auto)]\n        public static extern int CriticalFillRect(IntPtr hdc, ref NativeMethods.RECT rcFill, IntPtr brush);\n\n        [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]\n        public static extern int GetBitmapBits(HandleRef hbmp, int cbBuffer, byte[] lpvBits);\n\n        [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Auto)]\n        public static extern bool ShowWindow(HandleRef hWnd, int nCmdShow);\n\n        public static void DeleteObject(HandleRef hObject)\n        {\n            HandleCollector.Remove((IntPtr)hObject, NativeMethods.CommonHandles.GDI);\n\n            if (!IntDeleteObject(hObject))\n            {\n                throw new Win32Exception();\n            }\n        }\n\n        [DllImport(ExternDll.Gdi32, SetLastError = true, ExactSpelling = true, EntryPoint = \"DeleteObject\", CharSet = CharSet.Auto)]\n        public static extern bool IntDeleteObject(HandleRef hObject);\n\n        [DllImport(ExternDll.Gdi32, EntryPoint = \"SelectObject\", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]\n        public static extern IntPtr CriticalSelectObject(HandleRef hdc, IntPtr obj);\n\n        [DllImport(ExternDll.User32, EntryPoint = \"PrintWindow\", SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]\n        public static extern bool CriticalPrintWindow(HandleRef hWnd, HandleRef hDC, int flags);\n\n        [DllImport(ExternDll.User32, EntryPoint = \"RedrawWindow\", ExactSpelling = true, CharSet = CharSet.Auto)]\n        public static extern bool CriticalRedrawWindow(HandleRef hWnd, IntPtr lprcUpdate, IntPtr hrgnUpdate, int flags);\n\n        [DllImport(ExternDll.Shell32, CharSet = CharSet.Auto, BestFitMapping = false)]\n        public static extern IntPtr ShellExecute(HandleRef hwnd, string lpOperation, string lpFile, string lpParameters, string lpDirectory, int nShowCmd);\n","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/dotnet/wpf/blob/81131a70a4c573cd62748a5c36908fc4d662daa9/src/Microsoft.DotNet.Wpf/src/Shared/MS/Win32/UnsafeNativeMethodsCLR.cs#L83-L119","documentation":"UnsafeNativeMethodsCLR.DeleteObject wraps the Win32 DeleteObject GDI API. When IntDeleteObject returns false, the last Win32 error is not consulted and a bare Win32Exception is thrown, meaning GDI could not destroy the object handle. This library throws it to surface GDI handle-destruction failures rather than silently leaking or ignoring them.","triggerScenarios":"Calling DeleteObject on an already-deleted GDI handle, on a handle owned by another thread, or on a stock object (e.g. stock brushes/pens/fetch from GetStockObject) that cannot be deleted; also invalid HandleRef values passed from GDIExporter cleanup.","commonSituations":"Double-free of GDI handles during window/bitmap cleanup, finalizer racing with explicit disposal, deleting fonts/brushes still selected into a device context (HDC), running under GDI handle-pressure conditions.","solutions":["Ensure the GDI object is not still selected into an HDC (select the old object back in with SelectObject before DeleteObject).","Verify each handle is deleted exactly once — pair every Create*/Get* handle with a single DeleteObject and guard with a deleted flag.","Check that the handle was created on the same thread that deletes it, or marshal cleanup appropriately.","Inspect GetLastWin32Error via a debugger or temporarily call IntDeleteObject directly to learn the underlying error code (e.g. ERROR_INVALID_HANDLE)."],"exampleFix":"// before\nif (hBrush != IntPtr.Zero) { UnsafeNativeMethodsCLR.DeleteObject(new HandleRef(this, hBrush)); }\n// after\nif (hBrush != IntPtr.Zero && !_brushDeleted)\n{\n    UnsafeNativeMethodsCLR.SelectObject(hDC, oldBrush); // deselect first\n    UnsafeNativeMethodsCLR.DeleteObject(new HandleRef(this, hBrush));\n    _brushDeleted = true;\n}","handlingStrategy":"validation","validationCode":"if (handle == IntPtr.Zero || _deleted) throw new InvalidOperationException(\"GDI handle already released\");","typeGuard":"bool IsValidGdiHandle(IntPtr h) => h != IntPtr.Zero;","tryCatchPattern":"try { UnsafeNativeMethodsCLR.DeleteObject(new HandleRef(this, h)); }\ncatch (Win32Exception ex) { Trace.WriteLine($\"DeleteObject failed: {ex.NativeErrorCode}\"); }","preventionTips":["Select objects out of an HDC before deleting them","Track handle ownership so each GDI handle is deleted exactly once","Never delete stock objects (GetStockObject results)","Release handles on the thread that created them"],"tags":["win32","gdi","native-interop","handle-lifetime"],"backgroundTag":"win32-deleteobject-failed","analyzedSha":"81131a70a4c573cd62748a5c36908fc4d662daa9","analyzedAt":"2026-09-14T10:12:48.479Z","contentChangedAt":"2026-09-14T10:12:48.479Z","schemaVersion":2},"datasetVersion":"2026-09-22T01:17:13.364Z"}