java 使用JNA获取所有可见窗口

pepwfjgg  于 2023-01-19  发布在  Java
关注(0)|答案(1)|浏览(281)

我正在用Java编写一个覆盖图,显示多个窗口的信息。
我需要它跟踪它正在跟踪的窗口,为了做到这一点,我定期获取当前窗口的信息来更新我的覆盖层的位置。但我还需要知道窗口是否可见,如果不可见,我将隐藏覆盖层。理想情况下,我应该能够真实的完成所有这些工作,但我担心这对性能要求太高。
我和JNA一起做这一切

public interface User32 extends StdCallLibrary {
    User32 INSTANCE = (User32) Native.load("user32", User32.class, W32APIOptions.DEFAULT_OPTIONS);
    HWND FindWindow(String lpClassName, String lpWindowName);
    int GetWindowRect(HWND handle, int[] rect);
    boolean IsWindowVisible(HWND handle); // always true if window exist
}

public static int[] getWindowInformation(String windowName) {

    int[] rectangle = {0,0,0,0};

    HWND hwnd = User32.INSTANCE.FindWindow(null, windowName);

    User32.INSTANCE.GetWindowRect(hwnd, rectangle);
    boolean res = User32.INSTANCE.IsWindowVisible(hwnd);
    System.out.println(windowName + " is visible ? " + res);

    return rectangle;
}

这是我的代码,你可以看到,我尝试了“IsWindowVisible”后,完全阅读了JNA的User32 API,但它没有做我想要的。

mklgxw1f

mklgxw1f1#

User32使用IsWindowVisible的直觉是好的,JNA实际上已经在它的WindowUtils类中实现了这一点。

List<DesktopWindow> windows = WindowUtils.getAllWindows(true);

布尔参数为onlyVisibleWindows
注意,这里的“可见”指的是窗口本身的状态,而不是它是否被最小化(可能有离屏或“零”坐标)或“在其他窗口之后”与“在其他窗口之上”,从而对用户可见。
如果指定窗口、其父窗口、其父窗口的父窗口等具有WS_VISIBLE样式,则返回值为非零值。否则,返回值为零。
因为返回值指定窗口是否具有WS_VISIBLE样式,所以即使该窗口完全被其他窗口遮挡,它也可能是非零的。
为了确定窗口是部分还是全部被遮挡,你必须使用Z顺序来评估它的locAndSize矩形相对于它前面的所有窗口的大小。你可以逐个像素地进行评估,也可以只评估角点,这取决于你想要如何处理部分被遮挡的窗口。
JNA getAllWindows()方法返回封装以下字段的JNA DesktopWindow的列表(通过可见性过滤):

private HWND hwnd;
private String title;
private String filePath;
private Rectangle locAndSize;

在内部,您可以看到内部类W32WindowUtils中的实现,它使用发送到User32EnumWindows函数的回调函数:

@Override
public List<DesktopWindow> getAllWindows(final boolean onlyVisibleWindows) {
    final List<DesktopWindow> result = new LinkedList<DesktopWindow>();

    final WNDENUMPROC lpEnumFunc = new WNDENUMPROC() {
        @Override
        public boolean callback(final HWND hwnd, final Pointer arg1) {
            try {
                final boolean visible = !onlyVisibleWindows
                    || User32.INSTANCE.IsWindowVisible(hwnd);
                if (visible) {
                    final String title = getWindowTitle(hwnd);
                    final String filePath = getProcessFilePath(hwnd);
                    final Rectangle locAndSize = getWindowLocationAndSize(hwnd);
                    result.add(new DesktopWindow(hwnd, title, filePath,
                                                 locAndSize));
                }
            } catch (final Exception e) {
                // FIXME properly handle whatever error is raised
                e.printStackTrace();
            }

            return true;
        }
    };

    if (!User32.INSTANCE.EnumWindows(lpEnumFunc, null))
        throw new Win32Exception(Kernel32.INSTANCE.GetLastError());

    return result;
}

我维护了基于JNA的OSHI项目,并通过new SystemInfo().getOperatingSystem().getDesktopWindows()实现了这个跨平台的项目,new SystemInfo().getOperatingSystem().getDesktopWindows()在Windows上使用了上面的实现,并添加了来自其他函数的信息以获得窗口排序。OSHI的返回列表包括以下字段,并且返回的列表已经在order中排序,以帮助您确定用户可见性:

private final long windowId;
private final String title;
private final String command;
private final Rectangle locAndSize;
private final long owningProcessId;
private final int order;
private final boolean visible;

相关问题