{"record":{"id":"3ad94295ee691800","repo":"java-native-access/jna","slug":"device-context-did-not-release-properly","errorCode":null,"errorMessage":"Device context did not release properly.","messagePattern":"Device context did not release properly\\.","errorType":"exception","errorClass":"IllegalStateException","httpStatus":null,"severity":"warning","filePath":"contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java","lineNumber":187,"sourceCode":"                    }\n                    we = ex;\n                }\n            }\n\n            if (hdcTargetMem != null) {\n                // get rid of the device context when done\n                if (!GDI32.INSTANCE.DeleteDC(hdcTargetMem)) {\n                    Win32Exception ex = new Win32Exception(Native.getLastError());\n                    if (we != null) {\n                        ex.addSuppressedReflected(we);\n                    }\n                    we = ex;\n                }\n            }\n\n            if (hdcTarget != null) {\n                if (0 == User32.INSTANCE.ReleaseDC(target, hdcTarget)) {\n                    throw new IllegalStateException(\"Device context did not release properly.\");\n                }\n            }\n        }\n\n        if (we != null) {\n            throw we;\n        }\n        return image;\n    }\n}\n","sourceCodeStart":169,"sourceCodeEnd":198,"githubUrl":"https://github.com/java-native-access/jna/blob/d036ad9781adad4b66693e8fa7098e4ac665e0a3/contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java#L169-L198","documentation":"GDI32Util.getScreenshot throws this IllegalStateException when ReleaseDC returns 0, i.e. the device context obtained with GetDC could not be released. This indicates a GDI resource leak risk since each process has a limited DC quota.","triggerScenarios":"ReleaseDC failing because the HDC was already released, was not obtained via GetDC on that window, or the target HWND became invalid during the screenshot.","commonSituations":"Long-running automation taking many screenshots and hitting GDI handle exhaustion; double-release paths in custom code wrapped around getScreenshot; window destroyed between GetDC and ReleaseDC.","solutions":["Check for and fix double-release of the same HDC in surrounding code","Ensure the window remains valid for the duration of the capture (keep references, avoid destroying it concurrently)","Monitor GDI object count (Task Manager > GDI objects); if exhausted, release leaked DCs/bitmaps elsewhere in the app","Catch the IllegalStateException and log rather than masking an original screenshot failure — note the code rethrows the original Win32Exception afterwards if one occurred"],"exampleFix":"// before\nHDC hdc = User32.INSTANCE.GetDC(target);\nUser32.INSTANCE.ReleaseDC(target, hdc);\nUser32.INSTANCE.ReleaseDC(target, hdc); // second release -> fails\n// after\nHDC hdc = User32.INSTANCE.GetDC(target);\nif (hdc != null) {\n    User32.INSTANCE.ReleaseDC(target, hdc);\n}","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try {\n    BufferedImage img = GDI32Util.getScreenshot(hwnd);\n} catch (IllegalStateException e) {\n    if (e.getMessage().contains(\"Device context\")) {\n        log.warn(\"ReleaseDC failed; check GDI handle usage\", e);\n    } else {\n        throw e;\n    }\n}","preventionTips":["Do not release the same HDC twice in surrounding code","Keep the target window alive for the whole capture (no concurrent destroy)","Monitor process GDI object counts in long-running automation; leaks elsewhere can make ReleaseDC misbehave"],"tags":["java","win32","gdi","resource-leak","device-context"],"backgroundTag":"resource-release-failed","analyzedSha":"d036ad9781adad4b66693e8fa7098e4ac665e0a3","analyzedAt":"2026-09-12T06:50:59.239Z","contentChangedAt":"2026-09-12T06:50:59.239Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}