你从User32
开始使用IsWindowVisible
的直觉很好.JNA实际上已经在其WindowUtils类中实现了这一点.
List<DesktopWindow> windows = WindowUtils.getAllWindows(true);
布尔参数为onlyVisibleWindows
.
请注意,这里的"可见"指的是窗口本身的状态,而不是它是否被最小化(可能具有屏幕外或"零"坐标)或是否在其他窗口的"后面"而不是"顶部",从而对用户可见.摘自IsWindowVisible
份文件(注意第二段):
如果指定的窗口、其父窗口、其父窗口等的样式为WS_VISIBLE,则返回值为非零.否则,返回值为零.
由于返回值指定窗口是否具有WS_VISIBLE样式,因此即使该窗口完全被其他窗口遮挡,返回值也可能是非零值.
为了确定窗口是部分遮挡还是完全遮挡,您必须使用Z顺序计算相对于其前面的所有窗口的locAndSize
个矩形.您可以逐个像素地执行此操作,或者仅计算角点,具体取决于您希望如何处理部分遮挡的窗口.
JNA getAllWindows()
方法返回封装以下字段的JNA DesktopWindow
列表(按可见性过滤):
private HWND hwnd;
private String title;
private String filePath;
private Rectangle locAndSize;
在内部,您可以在内部类W32WindowUtils
中看到the implementation,它使用一个发送到User32
的EnumWindows
函数的回调函数:
@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()
实现了这个跨平台,它在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;