Windows:如何获取所有可见窗口的列表?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3188484/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Windows: how to get a list of all visible windows?
提问by NoozNooz42
(by all mean do re-tag with the relevant technology: I don't know which ones they are :)
(无论如何,请使用相关技术重新标记:我不知道它们是哪些:)
I'll probably come later with more detailed questions, about specific details but for now I'm trying to grasp the "big picture": I'm looking for a way to enumerate "real visible windows" on Windows. By "real visible window" I mean just that: what a user would call a "window". I need a way to get a list of all these visible windows, in Z-order.
稍后我可能会提出更详细的问题,关于具体细节,但现在我试图掌握“大局”:我正在寻找一种方法来枚举 Windows 上的“真实可见窗口”。“真正可见的窗口”我的意思是:用户会称之为“窗口”。我需要一种方法来以 Z 顺序获取所有这些可见窗口的列表。
Note that I doreally need to do that. I've already done it on OS X (where it is a real headache to do, especially if you want to support OS X 10.4, because OS X doesn't have convenient windows API) and now I need to do it under Windows.
请注意,我就真的需要做到这一点。我已经在 OS X 上完成了(这真的很头疼,特别是如果你想支持 OS X 10.4,因为 OS X 没有方便的 Windows API),现在我需要在 Windows 下完成。
Here's an example, suppose there are three visible windows on the screen, like this:
这是一个示例,假设屏幕上有三个可见窗口,如下所示:
+------------------------------------------+
| |
| +=============+ |
| | | |
| | A +--------------------------+
| | | |
| C | | B |
| | +--------------------------+
| | | |
+-----------| |----------------+
| |
+-------------+
Then I need to get back a list like this:
然后我需要取回这样的列表:
windows B is at (210,40)
windows A is at (120,20)
windows C is at (0,0)
Then if the user (or the OS) brings the window A to the front, it becomes:
然后,如果用户(或操作系统)将窗口 A 放在前面,它会变成:
+------------------------------------------+
| |
| +=============+ |
| | | |
| | A |---------------------+
| | | |
| C | | B |
| | |---------------------+
| | | |
+-----------| |----------------+
| |
+-------------+
And I get (ideally) a callback giving me this:
我得到(理想情况下)一个回调给我这个:
windows A is at (120,20)
windows B is at (210,40)
windows C is at (0,0)
Doing this under OS X requires the use of amazingly weird hacks (like mandating the user to turn on "Enable Access for assistive device"!) but I've done it under OS X and it works (under OS X, I didn't manage to get a callback everytime some window changes occurs, so I'm polling, but I got it to work).
在 OS X 下执行此操作需要使用非常奇怪的 hack(例如强制用户打开“启用辅助设备的访问”!)每次发生某些窗口更改时都会设法获得回调,所以我正在轮询,但我让它工作了)。
Now I want to do this under Windows (I really do, no question about it) and I've got a few questions:
现在我想在 Windows 下执行此操作(我真的这样做,毫无疑问)并且我有几个问题:
can this be done?
are there well documented Windows APIs (and working as per their specs) allowing to do that?
is it easy to register a callback everytime a window changes? (if it is resized, moved, brought to back/front or if a new window pops-up, etc.)
what would the gotchas be?
可以这样做吗?
是否有记录良好的 Windows API(并按照他们的规范工作)允许这样做?
每次窗口更改时都容易注册回调吗?(如果它被调整大小、移动、移到后面/前面或者如果一个新窗口弹出等)
问题是什么?
I know this question is not specific, which is why I've tried to describe my problem as clearly as possible (including nice ASCII art for which you can upvote this): for now I'm looking at the "big picture". I want to know what doing such a thing involves under Windows.
我知道这个问题并不具体,这就是为什么我试图尽可能清楚地描述我的问题(包括您可以为此投票的漂亮 ASCII 艺术):现在我正在研究“大局”。我想知道在Windows下做这样的事情涉及什么。
Bonus question: imagine you'd need to write a tiny .exewriting the windows names/position/size to a temporary file everytime there's a window change on screen, how long would such a program be approximately in your language of choice and how long would you need to write it?
额外问题:想象一下,每次屏幕上有窗口更改时,您都需要编写一个很小的.exe将窗口名称/位置/大小写入临时文件,这样的程序在您选择的语言中大约需要多长时间以及多长时间你需要写吗?
(once again, I'm trying to get the "big picture" to understand what is at work here)
(再一次,我试图获得“大局”以了解这里的工作原理)
采纳答案by mdma
To enumerate the top-level windows, you should use EnumWindowsrather than GetTopWindow/GetNextWindow, since EnumWindows returns a consistent view of the window state. You risk getting inconsistent information (such as reporting on deleted windows) or infinite loops using GetTopWindow/GetNextWindow, when windows change z-order during iteration.
要枚举顶级窗口,您应该使用EnumWindows而不是 GetTopWindow/GetNextWindow,因为 EnumWindows 返回窗口状态的一致视图。当窗口在迭代期间更改 z 顺序时,您可能会使用 GetTopWindow/GetNextWindow 获得不一致的信息(例如报告已删除的窗口)或无限循环。
The EnumWindows uses a callback. On each call of the callback you get a window handle. The screen co-ordinates of the window can be fetched by passing that handle to GetWindowRect. Your callback builds a list of the window positions in z-order.
EnumWindows 使用回调。在每次调用回调时,您都会获得一个窗口句柄。窗口的屏幕坐标可以通过将该句柄传递给GetWindowRect来获取。您的回调以 z 顺序构建窗口位置列表。
You can use polling, and build the window list repeatedly. Or, you set up a CBTHook to receive notifications of window changes. Not all CBT notifications will result in changes to order,position or visibility of top level windows, so it's wise to rerun EnmWindows to build a new list of window positions in z-order and compare this to the previous list before processing the list further, so that futher processing is done only when a real change has occurred.
您可以使用轮询,并重复构建窗口列表。或者,您设置 CBTHook 以接收窗口更改的通知。并非所有 CBT 通知都会导致顶级窗口的顺序、位置或可见性发生更改,因此明智的做法是重新运行 EnmWindows 以按 z 顺序构建新的窗口位置列表,并在进一步处理列表之前将其与之前的列表进行比较,这样只有在发生真正的变化时才进行进一步的处理。
Note that with hooking, you cannot mix 32- and 64-bit. If you are running a 32-bit app, then you will get notifications from 32-bit processes. Similarly for 64-bit. Thus, if you want to monitor the entire system on a 64-bit machine, it would seem that it's necessary to run two apps. My reasoning comes from reading this:
请注意,使用挂钩,您不能混合使用 32 位和 64 位。如果您运行的是 32 位应用程序,那么您将收到来自 32 位进程的通知。64 位也类似。因此,如果您想在 64 位机器上监控整个系统,似乎需要运行两个应用程序。我的推理来自阅读这个:
SetWindowsHookEx can be used to inject a DLL into another process. A 32-bit DLL cannot be injected into a 64-bit process, and a 64-bit DLL cannot be injected into a 32-bit process. If an application requires the use of hooks in other processes, it is required that a 32-bit application call SetWindowsHookEx to inject a 32-bit DLL into 32-bit processes, and a 64-bit application call SetWindowsHookEx to inject a 64-bit DLL into 64-bit processes. The 32-bit and 64-bit DLLs must have different names. (From the SetWindowsHookEx api page.)
SetWindowsHookEx 可用于将 DLL 注入另一个进程。32位DLL不能注入64位进程,64位DLL不能注入32位进程。如果应用程序需要在其他进程中使用钩子,则要求32位应用程序调用SetWindowsHookEx向32位进程注入32位DLL,64位应用程序调用SetWindowsHookEx注入64位DLL DLL 转换为 64 位进程。32 位和 64 位 DLL 必须具有不同的名称。(来自 SetWindowsHookEx api 页面。)
As you're implementing this in Java, you might want to look at JNA- it makes writing access to native libraries much simpler (calling code in java) and removes the need for your own native JNI DLL.
当您在 Java 中实现这一点时,您可能想要查看JNA- 它使对本机库的写入访问变得更加简单(在 Java 中调用代码)并且不需要您自己的本机 JNI DLL。
EDIT: You asked how much code it is and how long to write. Here's the code in java
编辑:你问它有多少代码以及编写多长时间。这是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) { // 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<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);
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);
}
}
}
I've made most of the related classes and interfaces inner classes to keep the example compact and pasteable for immediate compilation. In a real implementation, they would be regular top-level classes. The command line app prints out the visible windows and their position. I ran it on both 32-bit jvm and 64-bit, and got the same results for each.
我已经制作了大部分相关的类和接口内部类,以保持示例的紧凑和可粘贴以便立即编译。在实际实现中,它们将是常规的顶级类。命令行应用程序打印出可见窗口及其位置。我在 32 位 jvm 和 64 位上运行它,并得到相同的结果。
EDIT2: Updated code to include z-order. It does use GetNextWindow. In a production application, you should probably call GetNextWindow twice for the next and previous values and check they are consistent and are valid window handles.
EDIT2:更新代码以包含 z-order。它确实使用了 GetNextWindow。在生产应用程序中,您可能应该为下一个和上一个值调用两次 GetNextWindow 并检查它们是否一致并且是有效的窗口句柄。
回答by Billy ONeal
can this be done?
可以这样做吗?
Yes, though you'd have to register a hookin order to get what you want with regards to a callback. You would probably need to use a CBTProc Callback Hook, which is called whenever:
是的,尽管您必须注册一个钩子才能获得您想要的回调。您可能需要使用CBTProc Callback Hook,它在以下任何时候被调用:
activating, creating, destroying, minimizing, maximizing, moving, or sizing a window; before completing a system command; before removing a mouse or keyboard event from the system message queue; before setting the keyboard focus; or before synchronizing with the system message queue
激活、创建、销毁、最小化、最大化、移动或调整窗口大小;在完成系统命令之前;在从系统消息队列中删除鼠标或键盘事件之前;在设置键盘焦点之前;或在与系统消息队列同步之前
Note however that I don't believe such hooks work on Console windows because they are the domain of the kernel, not Win32.
但是请注意,我不相信这样的钩子在控制台窗口上工作,因为它们是内核的域,而不是 Win32。
are there well documented Windows APIs (and working as per their specs) allowing to do that?
是否有记录良好的 Windows API(并按照他们的规范工作)允许这样做?
Yes. You can use the GetTopWindowand GetNextWindowfunctions to get all the window handles on the desktop in correct Z order.
是的。您可以使用GetTopWindow和GetNextWindow函数以正确的 Z 顺序获取桌面上的所有窗口句柄。
is it easy to register a callback everytime a window changes? (if it is resized, moved, brought to back/front or if a new window pops-up, etc.)
每次窗口更改时都容易注册回调吗?(如果它被调整大小、移动、移到后面/前面或者如果一个新窗口弹出等)
See first answer :)
见第一个答案:)
what would the gotchas be?
问题是什么?
See first answer :)
见第一个答案:)
Bonus question: imagine you'd need to write a tiny .exe writing the windows names/position/size to a temporary file everytime there's a window change on screen, how long would such a program be approximately in your language of choice and how long would you need to write it?
额外问题:想象一下,每次屏幕上有窗口更改时,您都需要编写一个很小的 .exe 将窗口名称/位置/大小写入临时文件,这样的程序在您选择的语言中大约需要多长时间以及多长时间你需要写吗?
A few hundred lines of C, and a couple hours. Though I'd have to use some form of polling -- I've never done hooks before myself. If I'd need the hooks it'd take somewhat longer.
几百行 C 和几个小时。尽管我必须使用某种形式的轮询——我以前从未做过钩子。如果我需要挂钩,则需要更长的时间。
回答by Miro A.
I remember back in 2006 there was a utility WinObj as part of sysinternals that possibly did what you want. Part of these utilities were provided with source code by author (Mark Russinovich).
我记得在 2006 年有一个实用程序 WinObj 作为 sysinternals 的一部分,它可能会做你想做的事。这些实用程序的一部分由作者 (Mark Russinovich) 提供了源代码。
Since that time, his company was bought by Microsoft so I do not know whether the source would be still available.
从那以后,他的公司被微软收购了,所以我不知道源是否仍然可用。
Also the following may be worth checking:
此外,以下内容可能值得检查:
http://msdn.microsoft.com/en-us/library/aa264396(VS.60).aspx
http://msdn.microsoft.com/en-us/library/aa264396(VS.60).aspx