Windows: как получить список всех видимых окон? - PullRequest
43 голосов
/ 06 июля 2010

(во что бы то ни стало повторно пометить с соответствующей технологией: я не знаю, какие они:)

Я, вероятно, позже приду с более подробными вопросами, касающимися конкретных деталей, но сейчас я пытаюсь понять «общую картину»: я ищу способ перечисления «реально видимых окон» в Windows. Под «реально видимым окном» я имею в виду только то, что пользователь назвал бы «окном». Мне нужен способ получить список всех этих видимых окон в Z-порядке.

Обратите внимание, что я делаю действительно нужно сделать это. Я уже сделал это на OS X (где это настоящая головная боль, особенно если вы хотите поддерживать OS X 10.4, потому что OS X не имеет удобного Windows API), и теперь мне нужно сделать это под Windows.

Вот пример. Предположим, на экране есть три видимых окна, например:

 +------------------------------------------+
 |                                          |
 |           +=============+                |
 |           |             |                |
 |           |    A   +--------------------------+
 |           |        |                          |
 |    C      |        |             B            |
 |           |        +--------------------------+
 |           |             |                |
 +-----------|             |----------------+
             |             |
             +-------------+

Тогда мне нужно вернуть список, подобный этому:

 windows B is at (210,40)
 windows A is at (120,20)
 windows C is at (0,0)

Тогда, если пользователь (или ОС) переносит окно A на передний план, оно становится:

 +------------------------------------------+
 |                                          |
 |           +=============+                |
 |           |             |                |
 |           |    A        |---------------------+
 |           |             |                     |
 |    C      |             |        B            |
 |           |             |---------------------+
 |           |             |                |
 +-----------|             |----------------+
             |             |
             +-------------+

И я получаю (в идеале) ответный звонок, дающий мне это:

windows A is at (120,20)
windows B is at (210,40)
windows C is at (0,0)

Выполнение этого в OS X требует использования удивительно странных хаков (например, обязательное включение пользователем «Включить доступ для вспомогательного устройства» !), Но я сделал это в OS X, и оно работает (под OS X мне не удавалось получить обратный вызов каждый раз, когда происходят какие-либо изменения в окне, поэтому я опрашиваю, но я заставил его работать).

Теперь я хочу сделать это под Windows (правда, нет вопросов), и у меня есть несколько вопросов:

  • это можно сделать?

  • Есть ли хорошо документированные API-интерфейсы Windows (и работающие согласно их спецификациям), позволяющие это сделать?

  • Легко ли регистрировать обратный вызов при каждом изменении окна? (если он был изменен, перемещен, перенесен назад / вперед или если всплыло новое окно и т. д.)

  • кем бы были готы?

Я знаю, что этот вопрос не является конкретным, поэтому я попытался описать свою проблему настолько четко, насколько это возможно (включая красивое искусство ASCII, за которое вы можете выразить это): сейчас я смотрю на "общую картину" ». Я хочу знать, что делает такая вещь под Windows.

Дополнительный вопрос: представьте, что вам нужно написать крошечный .exe , записывающий имена / положение / размер окон во временный файл каждый раз, когда на экране изменяется окно, как долго будет длиться такая программа приблизительно на выбранном вами языке и сколько времени вам понадобится для его написания?

(еще раз, я пытаюсь получить "большую картину", чтобы понять, что здесь происходит)

Ответы [ 3 ]

27 голосов
/ 13 июля 2010

Чтобы перечислить окна верхнего уровня, вы должны использовать 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-битные библиотеки DLL должны иметь разные имена.(Со страницы API SetWindowsHookEx.)

Поскольку вы реализуете это в 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<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);
        }
    }
}

Я сделал большинство связанных классов и интерфейсов внутренними классами, чтобы сохранить пример компактным и вставляемым для немедленной компиляции.В реальной реализации это были бы обычные классы верхнего уровня.Приложение командной строки распечатывает видимые окна и их положение.Я запустил его как на 32-битном, так и на 64-битном jvm, и получил одинаковые результаты для каждого.

EDIT2: обновлен код для включения z-порядка.Он использует GetNextWindow.В производственном приложении вам, вероятно, следует дважды вызвать GetNextWindow для следующего и предыдущего значений и проверить, что они согласованы и являются допустимыми дескрипторами окна.

7 голосов
/ 08 июля 2010

можно ли это сделать?

Да, хотя вам нужно зарегистрировать hook , чтобы получить то, что вы хотите в отношении обратного вызова.Вам, вероятно, потребуется использовать CBTProc Callback Hook , который вызывается всякий раз, когда:

активирует, создает, уничтожает, минимизирует, максимизирует, перемещает или изменяет размер окна;перед выполнением системной команды;перед удалением события мыши или клавиатуры из очереди системных сообщений;перед настройкой фокуса клавиатуры;или перед синхронизацией с системной очередью сообщений

Обратите внимание, однако, что я не верю, что такие перехватчики работают в окнах консоли, поскольку они являются доменом ядра, а не Win32.

есть ли хорошо документированные API-интерфейсы Windows (и работающие согласно их спецификациям), позволяющие это сделать?

Да.Вы можете использовать функции GetTopWindow и GetNextWindow , чтобы получить все дескрипторы окон на рабочем столе в правильном порядке Z.

- это просто зарегистрировать обратный вызовкаждый раз окно меняется?(если он изменяется, перемещается, переносится назад / вперед или появляется новое окно и т. д.)

См. первый ответ:)

что бычто может быть?

См. первый ответ:)

Бонусный вопрос: представьте, что вам нужно написать крошечный .exe файл, записывающий имена / позиции / размеры окон ввременный файл каждый раз при смене окна на экране, как долго будет примерно такая программа на выбранном вами языке и сколько времени вам потребуется для ее написания?

Несколько сотен строк C,и пару часов.Хотя мне пришлось бы использовать какую-то форму опроса - я никогда раньше не делал хуков.Если бы мне понадобились крючки, это заняло бы немного больше времени.

1 голос
/ 13 июля 2010

Я помню, еще в 2006 году была утилита WinObj в составе sysinternals, которая, возможно, делала то, что вы хотите. Часть этих утилит была предоставлена ​​исходным кодом автора (Марк Руссинович).

С тех пор его компания была куплена Microsoft, поэтому я не знаю, будет ли источник по-прежнему доступен.

Также стоит проверить следующее:

http://msdn.microsoft.com/en-us/library/aa264396(VS.60).aspx

http://www.codeproject.com/KB/dialog/windowfinder.aspx

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...