Ответ 1
Чтобы перечислить окна верхнего уровня, вы должны использовать EnumWindows, а не GetTopWindow/GetNextWindow, поскольку EnumWindows возвращает согласованное представление состояния окна, Вы рискуете получить непоследовательную информацию (например, отчетность о удаленных окнах) или бесконечные циклы, используя GetTopWindow/GetNextWindow, когда окна меняют z-порядок во время итерации.
В 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-разрядные библиотеки должны иметь разные имена. (На странице SetWindowsHookEx api.)
Как вы реализуете это на Java, вы можете посмотреть JNA - он значительно упрощает запись в родные библиотеки (вызов кода в java) и устраняет необходимость в собственной собственной JNI-библиотеке.
EDIT: Вы спросили, сколько кода и сколько времени нужно писать. Здесь код в 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<WindowInfo> inflList = new ArrayList<WindowInfo>();
final List<Integer> order = new ArrayList<Integer>();
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) { // 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<WindowInfo>()
{
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);
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);
final int GW_HWNDNEXT = 2;
}
public static class RECT extends Structure {
public int left,top,right,bottom;
}
public static class WindowInfo {
int hwnd;
RECT rect;
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 для следующих и предыдущих значений и проверить, что они являются согласованными и являются допустимыми дескрипторами окон.