Home > Net >  Get all visible windows with JNA
Get all visible windows with JNA

Time:01-20

I am programming an overlay in Java showing information for multiple windows.

I need it to follow the window that it is tracking, and to do this I regularly take information about the current windows to update the position of my overlay. But I would also need to know if the windows are visible to hide the overlay if not. Ideally I should be able to do all this in real time but I'm afraid it's too performance-intensive.

I do all of this with 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;
}

Here is my code, you can see that I tried "IsWindowVisible" after read entirely the User32 API of JNA, but it doesn't do what I want.

CodePudding user response:

Your intuition to use IsWindowVisible from User32 is good. JNA has actually implemented this in its WindowUtils class.

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

The boolean parameter is onlyVisibleWindows.

Note that "visible" here refers to the state of the window itself, not whether it is minimized (may have off-screen or "zero" coordinate) or "behind" vs. "on top" of other windows and thus visible to the user. From the IsWindowVisible documentation (note the second paragraph):

If the specified window, its parent window, its parent's parent window, and so forth, have the WS_VISIBLE style, the return value is nonzero. Otherwise, the return value is zero.

Because the return value specifies whether the window has the WS_VISIBLE style, it may be nonzero even if the window is totally obscured by other windows.

In order to determine whether the window is partially or fully obscured, you would have to evaluate its locAndSize rectangle relative to all windows "in front" of it using Z-order. You could do this on a pixel-by-pixel basis or just evaluate the corner points, depending on how you want to handle partially obscured windows.

The JNA getAllWindows() method returns a list (filtered by visibility) of JNA DesktopWindows encapsulating these fields:

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

Internally you can see the implementation in the inner class W32WindowUtils, which uses a callback function sent to User32's EnumWindows function:

@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;
}

I maintain the JNA-based OSHI project, and have implemented this cross-platform via new SystemInfo().getOperatingSystem().getDesktopWindows(), which uses the above implementation on Windows and adds information from other functions to get the window ordering. OSHI's return list includes these fields and the returned list is already sorted in order to aid you in determining user visibility:

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;
  • Related