{"record":{"id":"2cc1aba1d6a1db84","repo":"Flow-Launcher/Flow.Launcher","slug":"failed-to-delete-hbitmap","errorCode":null,"errorMessage":"Failed to delete HBitmap.","messagePattern":"Failed to delete HBitmap\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"warning","filePath":"Plugins/Flow.Launcher.Plugin.Explorer/Helper/ShellContextMenuDisplayHelper.cs","lineNumber":389,"sourceCode":"                menuItems.Add(new ContextMenuItem(prefix + menuText, icon, mii.wID));\n            }\n        }\n    }\n\n    private static BitmapSource GetBitmapSourceFromHBitmap(IntPtr hBitmap)\n    {\n        try\n        {\n            var bitmapSource = Imaging.CreateBitmapSourceFromHBitmap(\n                hBitmap,\n                IntPtr.Zero,\n                Int32Rect.Empty,\n                BitmapSizeOptions.FromWidthAndHeight(16, 16)\n            );\n\n            if (!DeleteObject(hBitmap))\n            {\n                throw new Exception(\"Failed to delete HBitmap.\");\n            }\n\n            return bitmapSource;\n        }\n        catch (COMException)\n        {\n            // ignore\n        }\n\n        return null;\n    }\n}\n\n#region Data Structures\n\n[ComImport]\n[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]\n[Guid(\"000214E6-0000-0000-C000-000000000046\")]","sourceCodeStart":371,"sourceCodeEnd":407,"githubUrl":"https://github.com/Flow-Launcher/Flow.Launcher/blob/7fc63b07bbaa10a0f0b2601487913fcba9ed24b0/Plugins/Flow.Launcher.Plugin.Explorer/Helper/ShellContextMenuDisplayHelper.cs#L371-L407","documentation":"Thrown by GetBitmapSourceFromHBitmap when gdi32!DeleteObject returns false for the HBITMAP returned by the Shell in MENUITEMINFO.hbmpItem/hbmpChecked. CreateBitmapSourceFromHBitmap has already copied the pixels into a WPF BitmapSource, so this throw is purely a resource-leak-prevention check. Note the surrounding catch swallows only COMException, so this Exception propagates up and aborts the whole menu build.","triggerScenarios":"The HBITMAP is a shared/predefined handle that GDI refuses to delete (e.g. a stock object), the handle was already freed by another shell extension, the handle is actually an HICON or HBMP_FOREACH disguised, or DeleteObject is being called from a different desktop/session than the one that owns the object.","commonSituations":"A shell extension returns a cached system bitmap; an extension returns an HBITMAP wrapped via SHCreateShellFolderView that is not independently deletable; running under a different DPI awareness context so GDI handles belong to a different heap.","solutions":["Do not throw on DeleteObject failure: log a warning and continue. The pixels are already copied into the BitmapSource.","Broaden the catch to also swallow this Exception (currently only COMException is swallowed), so the rest of the menu still renders.","If the bitmap must be owned, copy it via CopyFromScreen or by locking the BitmapSource before attempting DeleteObject.","Detect shared handles (GetObjectType == OBJ_BITMAP stock) and skip deletion."],"exampleFix":"// before\nif (!DeleteObject(hBitmap))\n{\n    throw new Exception(\"Failed to delete HBitmap.\");\n}\n\n// after - do not let a non-owned handle abort the whole menu\nif (!DeleteObject(hBitmap))\n{\n    Main.Context.API.LogWarn(ClassName, $\"Unable to delete shell HBITMAP 0x{hBitmap.ToInt64():X}; it may be shared.\");\n}","handlingStrategy":"try-catch","validationCode":"// GDI ownership cannot be pre-checked; the safe guard is to not throw on failure.","typeGuard":null,"tryCatchPattern":"// Broaden the catch in GetBitmapSourceFromHBitmap so a non-owned handle cannot abort the whole menu.\ntry { ... if (!DeleteObject(hBitmap)) Main.Context.API.LogWarn(ClassName, $\"Unable to delete HBITMAP 0x{hBitmap.ToInt64():X}\"); }\ncatch (Exception ex) { Main.Context.API.LogWarn(ClassName, $\"Bitmap conversion failed: {ex.Message}\"); }","preventionTips":["Never throw from DeleteObject - the pixels are already copied.","Log non-owned/shared handles and continue.","Detect stock objects (GetObjectType) and skip deletion.","Copy the bitmap into WPF before deleting, which the code already does."],"tags":["win32","gdi","hbitmap","context-menu","csharp"],"backgroundTag":null,"analyzedSha":"7fc63b07bbaa10a0f0b2601487913fcba9ed24b0","analyzedAt":"2026-08-13T14:54:20.914Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}