java-native-access/jna · error · IllegalStateException

Window width and/or height were 0 even though GetWindowRect

Error message

Window width and/or height were 0 even though GetWindowRect did not appear to fail.

What it means

GDI32Util.getScreenshot throws this IllegalStateException when GetWindowRect reported success but the resulting rectangle has zero width or height, making a bitmap capture impossible. It is a sanity check guarding Blt/copy operations that would fail or produce an empty image.

Source

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

     * @return the window captured as a screenshot, or null if the BufferedImage
     *         doesn't construct properly
     *
     * @throws IllegalStateException if the rectangle from GetWindowRect has a
     *                               width and/or height of 0. <br>
     * if the device context acquired from the original HWND doesn't release
     * properly
     */
    public static BufferedImage getScreenshot(HWND target) {
        RECT rect = new RECT();
        if (!User32.INSTANCE.GetWindowRect(target, rect)) {
            throw new Win32Exception(Native.getLastError());
        }
        Rectangle jRectangle = rect.toRectangle();
        int windowWidth = jRectangle.width;
        int windowHeight = jRectangle.height;

        if (windowWidth == 0 || windowHeight == 0) {
            throw new IllegalStateException("Window width and/or height were 0 even though GetWindowRect did not appear to fail.");
        }

        HDC hdcTarget = User32.INSTANCE.GetDC(target);
        if (hdcTarget == null) {
            throw new Win32Exception(Native.getLastError());
        }

        Win32Exception we = null;

        // device context used for drawing
        HDC hdcTargetMem = null;

        // handle to the bitmap to be drawn to
        HBITMAP hBitmap = null;

        // original display surface associated with the device context
        HANDLE hOriginal = null;

View on GitHub (pinned to d036ad9781)

Solutions

  1. Ensure the target window is visible/restored before capture (ShowWindow with SW_RESTORE, or wait for it to be shown)
  2. Check IsWindowVisible and IsIconic and restore/bring the window forward first
  3. Verify the HWND is the correct top-level window, not a zero-size child or dummy handle
  4. If the zero size is legitimate for your case, skip the screenshot instead of calling getScreenshot

Example fix

// before
BufferedImage img = GDI32Util.getScreenshot(hwnd);
// after
if (!User32.INSTANCE.IsWindowVisible(hwnd) || User32.INSTANCE.IsIconic(hwnd)) {
    User32.INSTANCE.ShowWindow(hwnd, new WinDef.SW_NOSHOW(SW_RESTORE));
}
RECT rect = new RECT();
User32.INSTANCE.GetWindowRect(hwnd, rect);
if (rect.toRectangle().width > 0 && rect.toRectangle().height > 0) {
    BufferedImage img = GDI32Util.getScreenshot(hwnd);
}
Defensive patterns

Strategy: validation

Validate before calling

RECT r = new RECT();
if (!User32.INSTANCE.GetWindowRect(hwnd, r)) throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
if (r.toRectangle().width == 0 || r.toRectangle().height == 0) {
    User32.INSTANCE.ShowWindow(hwnd, 9 /* SW_RESTORE */);
}

Type guard

boolean isScreenshottable(WinDef.HWND hwnd) {
    RECT r = new RECT();
    return User32.INSTANCE.IsWindowVisible(hwnd)
        && !User32.INSTANCE.IsIconic(hwnd)
        && User32.INSTANCE.GetWindowRect(hwnd, r)
        && r.toRectangle().width > 0 && r.toRectangle().height > 0;
}

Try / catch

try {
    BufferedImage img = GDI32Util.getScreenshot(hwnd);
} catch (IllegalStateException e) {
    // window zero-size: restore and retry once
    User32.INSTANCE.ShowWindow(hwnd, 9);
    BufferedImage img = GDI32Util.getScreenshot(hwnd);
}

Prevention

When it happens

Trigger: Capturing a window that is minimized, not yet shown (zero-size client area), or whose rect conversion yields 0 dimensions; passing an HWND of a hidden window.

Common situations: Screenshotting a minimized window via automation; capturing during window creation before layout; targeting a message-only or zero-size window.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


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