You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用JNA获取可见窗口,解决Java窗口Overlay追踪的可见性判断问题

解决方案:准确判断窗口可见性+优化Overlay更新性能

一、替代IsWindowVisible的窗口可见性判断逻辑

IsWindowVisible仅检查窗口的WS_VISIBLE标志,无法区分窗口是否被最小化、遮挡或处于非当前桌面。结合以下API可以实现更准确的判断:

1. 新增必要的JNA映射

先补充User32接口和相关结构体定义:

import com.sun.jna.Structure;
import com.sun.jna.platform.win32.WinDef.*;
import com.sun.jna.win32.StdCallLibrary;
import java.util.Arrays;
import java.util.List;

public interface User32 extends StdCallLibrary {
    User32 INSTANCE = Native.load("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS);

    // 原有方法
    HWND FindWindow(String lpClassName, String lpWindowName);
    boolean GetWindowRect(HWND handle, RECT rect);
    boolean IsWindowVisible(HWND handle);

    // 新增方法和常量
    boolean IsIconic(HWND hWnd);
    boolean GetWindowPlacement(HWND hWnd, WINDOWPLACEMENT lpwndpl);

    int SW_HIDE = 0;
    int SW_MINIMIZE = 6;

    class WINDOWPLACEMENT extends Structure {
        public int length = size();
        public int flags;
        public int showCmd;
        public POINT ptMinPosition;
        public POINT ptMaxPosition;
        public RECT rcNormalPosition;

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList("length", "flags", "showCmd", "ptMinPosition", "ptMaxPosition", "rcNormalPosition");
        }
    }

    class POINT extends Structure {
        public int x;
        public int y;

        @Override
        protected List<String> getFieldOrder() {
            return Arrays.asList("x", "y");
        }
    }
}

2. 完整的可见性判断逻辑

结合多个API实现精准判断:

public static boolean isWindowActuallyVisible(String windowName) {
    HWND hwnd = User32.INSTANCE.FindWindow(null, windowName);
    if (hwnd == null) {
        return false;
    }

    // 基础可见性标志检查
    if (!User32.INSTANCE.IsWindowVisible(hwnd)) {
        return false;
    }

    // 排除最小化窗口
    if (User32.INSTANCE.IsIconic(hwnd)) {
        return false;
    }

    // 检查窗口显示状态
    User32.WINDOWPLACEMENT wp = new User32.WINDOWPLACEMENT();
    if (User32.INSTANCE.GetWindowPlacement(hwnd, wp)) {
        return wp.showCmd != User32.SW_HIDE && wp.showCmd != User32.SW_MINIMIZE;
    }

    return false;
}

二、优化Overlay更新的性能开销

1. 合理设置轮询间隔

如果采用轮询方案,避免过高频率,推荐200-500ms一次,用定时任务实现:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

ScheduledExecutorService updateExecutor = Executors.newSingleThreadScheduledExecutor();
updateExecutor.scheduleAtFixedRate(() -> {
    // 更新Overlay位置和可见性
    updateOverlayPositionAndVisibility();
}, 0, 300, TimeUnit.MILLISECONDS);

2. 事件驱动更新(推荐)

通过监听Windows窗口消息,仅在窗口状态变化时触发更新,避免无效轮询。可以通过设置窗口钩子监听目标窗口的移动、大小变化、显示状态变更等事件:

// 为目标窗口设置消息回调(简化示例)
User32.WindowProc proc = (hWnd, msg, wParam, lParam) -> {
    switch (msg) {
        case User32.WM_MOVE:
        case User32.WM_SIZE:
        case User32.WM_SHOWWINDOW:
            // 窗口状态变化,更新Overlay
            updateOverlayPositionAndVisibility();
            break;
    }
    return User32.INSTANCE.CallWindowProc(oldProc, hWnd, msg, wParam, lParam);
};

User32.WindowProc oldProc = User32.INSTANCE.SetWindowLongPtr(targetHwnd, User32.GWL_WNDPROC, proc);

三、修正原代码的潜在问题

原代码未判断窗口句柄有效性,容易引发崩溃,修改后:

public static RECT getWindowInformation(String windowName) {
    RECT rect = new RECT();
    HWND hwnd = User32.INSTANCE.FindWindow(null, windowName);
    if (hwnd != null) {
        User32.INSTANCE.GetWindowRect(hwnd, rect);
    }
    return rect;
}

内容的提问来源于stack exchange,提问作者Noah Fraiture

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 04:25:16