要枚举顶级窗口,应该使用EnumWindows而不是GetTopWindow / GetNextWindow,因为EnumWindows返回窗口状态的一致视图。 当窗口在迭代过程中更改z顺序时,您可能会面临获得不一致的信息(例如,有关已删除窗口的报告)或使用GetTopWindow / GetNextWindow导致无限循环的风险。
EnumWindows使用回调。 在回调的每次调用中,您都会获得一个窗口句柄。 可以通过将该句柄传递给GetWindowRect来获取窗口的屏幕坐标。 您的回调将以z顺序构建窗口位置的列表。
您可以使用轮询,然后重复构建窗口列表。 或者,您可以设置一个CBTHook来接收窗口更改的通知。 并非所有的CBT通知都会导致顶级窗口的顺序,位置或可见性发生变化,因此明智的做法是重新运行EnmWindows以按z顺序构建新的窗口位置列表,并将其与上一个列表进行比较,然后再进一步处理该列表, 以便仅在发生实际更改时才进行进一步处理。
请注意,使用钩子不能将32位和64位混合使用。 如果您运行的是32位应用程序,则将收到32位进程的通知。 同样适用于64位。 因此,如果要在64位计算机上监视整个系统,则似乎有必要运行两个应用程序。 我的推理来自阅读以下内容:
SetWindowsHookEx可用于注入 一个DLL进入另一个进程。 32位 DLL无法注入64位 进程,并且不能将64位DLL 注入到32位进程中。 如果 应用程序需要使用挂钩 在其他过程中,这是必需的 一个32位应用程序调用 SetWindowsHookEx注入32位 DLL转换为32位进程,并且 64位应用程序调用 SetWindowsHookEx注入64位 DLL转换为64位进程。 32位 和64位DLL必须具有不同的 名称。 (从SetWindowsHookEx api页面。)
在用Java实现此过程时,您可能需要看一下JNA-它使对本机库的写访问更加简单(在Java中调用代码),并且不需要您自己的本机JNI DLL。
编辑:您问它是多少代码和要写多长时间。 这是java中的代码
import com.sun.jna.Native;
import com.sun.jna.Structure;
import com.sun.jna.win32.StdCallLibrary;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
Main m = new Main();
final List inflList = new ArrayList();
final List order = new ArrayList();
int top = User32.instance.GetTopWindow(0);
while (top != 0) {
order.add(top);
top = User32.instance.GetWindow(top, User32.GW_HWNDNEXT);
}
User32.instance.EnumWindows(new WndEnumProc() {
public boolean callback(int hWnd, int lParam) {
if (User32.instance.IsWindowVisible(hWnd)) {
RECT r = new RECT();
User32.instance.GetWindowRect(hWnd, r);
if (r.left > -32000) { // If it's not minimized
byte[] buffer = new byte[1024];
User32.instance.GetWindowTextA(hWnd, buffer, buffer.length);
String title = Native.toString(buffer);
inflList.add(new WindowInfo(hWnd, r, title));
}
}
return true;
}
}, 0);
Collections.sort(inflList, new Comparator() {
public int compare(WindowInfo o1, WindowInfo o2) {
return order.indexOf(o1.hwnd)-order.indexOf(o2.hwnd);
}
});
for (WindowInfo w : inflList) {
System.out.println(w);
}
}
public static interface WndEnumProc extends StdCallLibrary.StdCallCallback {
boolean callback(int hWnd, int lParam);
}
public static interface User32 extends StdCallLibrary {
final User32 instance = (User32) Native.loadLibrary ("user32", User32.class);
final int GW_HWNDNEXT = 2;
boolean EnumWindows(WndEnumProc wndenumproc, int lParam);
boolean IsWindowVisible(int hWnd);
int GetWindowRect(int hWnd, RECT r);
void GetWindowTextA(int hWnd, byte[] buffer, int buflen);
int GetTopWindow(int hWnd);
int GetWindow(int hWnd, int flag);
}
public static class RECT extends Structure {
public int left, top, right, bottom;
}
public static class WindowInfo {
public final int hwnd;
public final RECT rect;
public final String title;
public WindowInfo(int hwnd, RECT rect, String title) {
this.hwnd = hwnd;
this.rect = rect;
this.title = title;
}
public String toString() {
return String.format("(%d,%d)-(%d,%d) : \"%s\"",
rect.left, rect.top,
rect.right, rect.bottom,
title);
}
}
}
我制作了大多数相关类和接口内部类,以使示例紧凑且可粘贴以立即进行编译。 在实际的实现中,它们将是常规的顶级类。 命令行应用程序将打印出可见窗口及其位置。 我在32位jvm和64位上都运行了它,并且得到了相同的结果。
EDIT2:更新了代码以包括z顺序。 它确实使用GetNextWindow。 在生产应用程序中,您可能应该为下一个和上一个值两次调用GetNextWindow,并检查它们是否一致并且是有效的窗口句柄。