2010-07-06 3 views
39

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

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

Отметьте, что I 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 искусства, для которого вы можете upvote это): сейчас я смотрю на большая картина". Я хочу знать, что такое происходит под Windows.

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

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

+0

Никто? Уже +1 upvote и 1 любимый ... :) – NoozNooz42

+0

Ну, я думаю, вы начнете с FindWindowEx, чтобы перечислять все окна в Z-порядке, а затем использовать GetWindowLong, чтобы получить стиль окна. Очевидно, что вы будете рассматривать только окна с WS_VISIBLE и, возможно, WS_CAPTION или что-то в этом роде. – Luke

+0

Есть .net framework вариант? У меня есть простое решение для этого, занимает десять секунд. – Cyclone

ответ

23

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

Как вы реализуете это в Java, вы можете захотеть взглянуть на JNA - это делает написание доступа к родной библиотеке гораздо проще (код вызова в Java) и удаляешь нужна ваша собственная JNI DLL.

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); 
     } 
    } 
} 

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

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

+0

просто, если я просто опросу, то я могу просто использовать EnumWindows и нет 32-/64-разрядной проблемы? JNA был планом (у них был пример, перечисляющий окна на Windows btw, мне просто нужно его найти), но сначала я хотел знать, что было «под капотом». Я до сих пор не знаю, есть ли у меня навыки, чтобы сделать это сам (я никогда не программировал Windows), но все эти ответы очень помогают мне понять, что происходит! – NoozNooz42

+0

@ NoozNooz42 - Правильно, опрос позволяет избежать использования крючков и 32/64 бит. Вот статья, которая вызывает EnumWindows из JNA. http://javajeff.mb.ca/cgi-bin/mp.cgi?/java/javase/articles/mjnae – mdma

+0

@mdna: Проблема с EnumWindows заключается в том, что он не возвращает окна в порядке Z. (Или, по крайней мере, не соответствует его документации) –

7

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

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

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

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

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

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

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

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

что бы быть подводные камни?

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

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

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

+0

большое спасибо за этот ответ ... Так что, если я правильно вас пойму, вы будете опросить (это то, что я делаю под OS X), а затем нет крючков и никакого обратного вызова ** ИЛИ ** вы будете использовать перехватчики, позволяя иметь обратный вызов? (еще раз, в OS X AFAICT нет простого способа сделать это с помощью обратного вызова, поэтому я опрос). – NoozNooz42

+1

@Nooz: Крючок * является * обратным вызовом. –

+1

@Nooz: Иными словами, да. Вы можете опросить или использовать крючок в качестве обратного вызова. Крюк лучше, но я просто оценил время, которое потребуется, поскольку раньше я никогда не использовал крючки. –

1

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

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

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

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

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

Смежные вопросы