Windows: как получить список всех видимых окон?

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

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

Обратите внимание, что мне действительно нужно выполнить do. Я уже делал это на OS X (где это настоящая головная боль, особенно если вы хотите поддерживать OS X 10.4, потому что OS X не имеет удобного 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, который записывает имена/положение/размер окна во временный файл каждый раз, когда на экране появляется изменение окна, как долго эта программа будет примерно на вашем языке выбора и сколько времени вам нужно будет написать?

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

Ответ 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 для следующих и предыдущих значений и проверить, что они являются согласованными и являются допустимыми дескрипторами окон.

Ответ 2

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

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

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

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

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

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

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

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

что бы получилось?

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

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

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

Ответ 3

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

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

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

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

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