java-native-access/jna · warning · IllegalStateException

Device context did not release properly.

Error message

Device context did not release properly.

What it means

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.

Source

Thrown at contrib/platform/src/com/sun/jna/platform/win32/GDI32Util.java:187

                    }
                    we = ex;
                }
            }

            if (hdcTargetMem != null) {
                // get rid of the device context when done
                if (!GDI32.INSTANCE.DeleteDC(hdcTargetMem)) {
                    Win32Exception ex = new Win32Exception(Native.getLastError());
                    if (we != null) {
                        ex.addSuppressedReflected(we);
                    }
                    we = ex;
                }
            }

            if (hdcTarget != null) {
                if (0 == User32.INSTANCE.ReleaseDC(target, hdcTarget)) {
                    throw new IllegalStateException("Device context did not release properly.");
                }
            }
        }

        if (we != null) {
            throw we;
        }
        return image;
    }
}

View on GitHub (pinned to d036ad9781)

Solutions

  1. Check for and fix double-release of the same HDC in surrounding code
  2. Ensure the window remains valid for the duration of the capture (keep references, avoid destroying it concurrently)
  3. Monitor GDI object count (Task Manager > GDI objects); if exhausted, release leaked DCs/bitmaps elsewhere in the app
  4. Catch the IllegalStateException and log rather than masking an original screenshot failure — note the code rethrows the original Win32Exception afterwards if one occurred

Example fix

// before
HDC hdc = User32.INSTANCE.GetDC(target);
User32.INSTANCE.ReleaseDC(target, hdc);
User32.INSTANCE.ReleaseDC(target, hdc); // second release -> fails
// after
HDC hdc = User32.INSTANCE.GetDC(target);
if (hdc != null) {
    User32.INSTANCE.ReleaseDC(target, hdc);
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    BufferedImage img = GDI32Util.getScreenshot(hwnd);
} catch (IllegalStateException e) {
    if (e.getMessage().contains("Device context")) {
        log.warn("ReleaseDC failed; check GDI handle usage", e);
    } else {
        throw e;
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of java-native-access/jna@d036ad9781 (2026-09-12). Data as JSON: /api/errors/3ad94295ee691800. Report an issue: GitHub.