Windows:如何获取所有可见窗口的列表? [英] Windows: how to get a list of all visible windows?

查看:103
本文介绍了Windows:如何获取所有可见窗口的列表?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

(通过所有意思重新标记相关技术:我不知道它们是哪一个:)

I我可能会在稍后提出更详细的问题,关于具体的细节但是现在我正试图抓住大局:我正在寻找一种在Windows上枚举真实可见窗口的方法。通过真实可见窗口我的意思是:用户称之为窗口。我需要一种方法来获取所有这些可见窗口的列表,按Z顺序。

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.

请注意,我确实需要要做到这一点。我已经在OS X上完成了它(这是一个非常令人头痛的事情,特别是如果你想支持OS X 10.4,因为OS X没有方便的Windows API),现在我需要在Windows下完成它。

Note that I do really 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.

这是一个例子,假设屏幕上有三个可见窗口,如下所示:

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)

然后如果用户(或操作系统)将窗口A带到前面,它变为:

Then if the user (or the OS) brings the window A to the front, it becomes:

 +------------------------------------------+
 |                                          |
 |           +=============+                |
 |           |             |                |
 |           |    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)

这样做在OS X下需要使用非常奇怪的黑客(比如强制用户打开启用辅助设备的访问权限!)但我已经在OS X下完成它并且它可以工作(在OS X下)每次发生一些窗口更改时我都没有设法回调,所以我正在进行轮询,但是我让它工作了。)

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).

现在我想做这在Windows下(我真的这么做,毫无疑问)并且我有几个问题:

Now I want to do this under Windows (I really do, no question about it) and I've got a few questions:


  • 这可以是完成了吗?

  • can this be done?

是否有很好的文档Windows API(按照他们的规格工作)允许这样做?

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.)

会有什么问题?

我知道这个问题并不具体,这就是为什么我试图尽可能清楚地描述我的问题(包括很好的ASCII艺术,你可以赞成这个):现在我正在看大局。我想知道在Windows下做这样的事情是什么。

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.

奖金问题:想象你需要写一个小的 .exe 写作每当窗口更改窗口时,窗口名称/位置/大小为临时文件,这样的程序将以您选择的语言大约需要多长时间以及需要多长时间才能编写它?

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?

(再一次,我想要了解大局以了解这里的工作原理)

推荐答案

要枚举顶级窗口,您应该使用 EnumWindows 而不是GetTopWindow / GetNextWindow,因为EnumWindows返回窗口状态的一致视图。当窗口在迭代期间更改z顺序时,您可能会使用GetTopWindow / GetNextWindow获取不一致的信息(例如报告已删除的窗口)或无限循环。

To enumerate the top-level windows, you should use EnumWindows rather 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使用回调。在回调的每次调用中,您都会获得一个窗口句柄。可以通过将该句柄传递给 GetWindowRect 。您的回调以z顺序构建窗口位置列表。

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.

您可以使用轮询,并重复构建窗口列表。或者,您设置CBTHook以接收窗口更改的通知。并非所有CBT通知都会导致顶级窗口的顺序,位置或可见性发生变化,因此重新运行EnmWindows以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.

请注意,通过挂钩,您不能混合使用32位和64位。如果您运行的是32位应用程序,那么您将从32位进程获得通知。同样适用于64位。因此,如果要在64位计算机上监视整个系统,似乎需要运行两个应用程序。我的推理来自于阅读:

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:


SetWindowsHookEx可用于将
DLL注入另一个进程。 32位
DLL无法注入64位
进程,而64位DLL不能注入32位进程
。如果
应用程序需要在其他进程中使用钩子
,那么32位应用程序调用
SetWindowsHookEx来注入32位
DLL需要
进入32位进程,
64位应用程序调用
SetWindowsHookEx将64位
DLL注入到64位进程中。 32位
和64位DLL必须具有不同的
名称。
(来自SetWindowsHookEx api页面。)

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.)

当你在Java中实现它时,你可能想看看 JNA - 它使得对本机库的写入访问变得更加简单(在java中调用代码)并且不再需要你自己的原生JNI DLL。

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中的代码

You asked how much code it is and how long to write. Here's the code in 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);
        }
    }
}

我赚了很多钱相关的类和接口内部类,以保持示例紧凑和可粘贴,以便立即编译。在实际的实现中,它们将是常规的顶级类。命令行app打印出可见窗口及其位置。我在32位jvm和64位上运行它,并且每个都得到相同的结果。

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.

EDIT2:更新了包含z-order的代码。它确实使用GetNextWindow。在生产应用程序中,您可能应该为下一个和上一个值调用GetNextWindow两次,并检查它们是否一致并且是有效的窗口句柄。

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.

这篇关于Windows:如何获取所有可见窗口的列表?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆