2010-07-06 33 views
39

(por todos significa hacer re-etiqueta con la tecnología relevante: No sé cuáles son :)Windows: ¿cómo obtener una lista de todas las ventanas visibles?

que probablemente vienen después con preguntas más detalladas sobre los detalles específicos, pero por ahora Estoy tratando de comprender el "panorama general": estoy buscando una manera de enumerar "ventanas visibles reales" en Windows. Por "ventana visible real" quiero decir exactamente eso: lo que un usuario llamaría una "ventana". Necesito una forma de obtener una lista de todas estas ventanas visibles, en orden Z.

Tenga en cuenta que I do realmente necesito hacer eso. Ya lo hice en OS X (donde es un verdadero dolor de cabeza, especialmente si desea soportar OS X 10.4, porque OS X no tiene la conveniente API de Windows) y ahora tengo que hacerlo en Windows.

He aquí un ejemplo, supongamos que hay tres ventanas visibles en la pantalla, así:

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

entonces necesito para volver una lista como esta:

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

A continuación, si el el usuario (o el sistema operativo) lleva la ventana A al frente, se convierte en:

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

y yo get (idealmente) una devolución de llamada darme esta:

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

Hacer esto en OS X requiere el uso de hacks increíblemente extraños (como que obliga al usuario a activar "Activar acceso para dispositivos de ayuda"!) pero Lo he hecho con OS X y funciona (bajo OS X, no pude obtener una devolución de llamada cada vez que se producen cambios en la ventana, entonces estoy sondeando, pero lo hice funcionar).

Ahora quiero hacer esto en Windows (realmente, no hay duda al respecto) y tengo algunas preguntas:

  • puede hacerse esto?

  • ¿hay API Windows bien documentadas (y funcionan de acuerdo con sus especificaciones) que permiten hacer eso?

  • ¿Es fácil registrar una devolución de llamada cada vez que cambia una ventana? (si se cambia de tamaño, se mueve, se lleva hacia atrás/frente o si aparece una nueva ventana emergente, etc.)

  • ¿cuáles serían las trampas?

Sé que esta pregunta no es específico, por lo que he tratado de describir mi problema lo más claramente posible (incluyendo el arte ASCII agradable para el que puede upvote esto): por ahora estoy mirando el panorama". Quiero saber qué hace eso bajo Windows.

pregunta

Bono: imagina que había necesidad de escribir una pequeña .exe escribir los nombres de las ventanas/posición/tamaño a un archivo temporal cada vez que hay un cambio de ventana en la pantalla, ¿cuánto tiempo un programa de este tipo será aproximadamente en su idioma de elección y por cuánto tiempo necesitarías escribirlo?

(una vez más, estoy tratando de conseguir el "cuadro grande" para entender lo que está en juego aquí)

+0

nadie? Ya + 1 voto positivo y 1 favorito ... :) – NoozNooz42

+0

Bueno, supongo que comenzaría con FindWindowEx para enumerar todas las ventanas en orden Z y luego usar GetWindowLong para obtener el estilo de ventana. Obviamente, solo consideraría Windows con WS_VISIBLE, y quizás WS_CAPTION o algo así. – Luke

+0

¿Es .net framework una opción? Tengo una solución simple para esto, toma como diez segundos. – Cyclone

Respuesta

23

Enumerar las ventanas de nivel superior, se debe utilizar EnumWindows en lugar de GetTopWindow/GetNextWindow, ya EnumWindows devuelve una visión consistente del estado de la ventana. Corre el riesgo de obtener información inconsistente (como informar sobre ventanas eliminadas) o bucles infinitos usando GetTopWindow/GetNextWindow, cuando las ventanas cambian de orden z durante la iteración.

EnumWindows usa una devolución de llamada. En cada llamada de la devolución de llamada se obtiene un identificador de ventana. Las coordenadas de pantalla de la ventana se pueden obtener pasando ese identificador a GetWindowRect. Su devolución de llamada crea una lista de las posiciones de las ventanas en orden z.

Puede usar sondeo y compilar la lista de ventanas repetidamente. O bien, configura un CBTHook para recibir notificaciones de cambios de ventana. No todas las notificaciones CBT darán lugar a cambios en el orden, posición o visibilidad de las ventanas de nivel superior, por lo que es aconsejable volver a EnmWindows para compilar una nueva lista de posiciones de ventana en orden z y comparar esto con la lista anterior antes de continuar con la lista, de modo que un procesamiento posterior solo se realiza cuando se produce un cambio real.

Tenga en cuenta que con el enganche, no puede mezclar 32 y 64 bits. Si está ejecutando una aplicación de 32 bits, recibirá notificaciones de procesos de 32 bits. Del mismo modo para 64 bits. Por lo tanto, si desea monitorear todo el sistema en una máquina de 64 bits, parecería que es necesario ejecutar dos aplicaciones. Mi razonamiento proviene de la lectura de este:

SetWindowsHookEx se puede utilizar para inyectar una DLL en otro proceso. No se puede inyectar un DLL de 32 bits en un proceso de 64 bits, y un DLL de 64 bits no puede ser inyectado en un proceso de 32 bits. Si una aplicación requiere el uso de ganchos en otros procesos, se requiere que una llamada aplicación de 32 bits SetWindowsHookEx para inyectar una DLL de 32 bits en los procesos de 32 bits, y una llamada de aplicación 64 bits SetWindowsHookEx para inyectar una DLL de 64 bits en procesos de 64 bits. Los archivos DLL de 32 bits y 64 bits deben tener nombres diferentes. (. Desde la página de api SetWindowsHookEx)

Como se va a implementar esto en Java, es posible que desee ver en JNA - que hace que escribir el acceso a bibliotecas nativas mucho más simple (código en Java que llaman) y elimina la necesita su propia DLL JNI nativa.

EDITAR: Usted preguntó cuánto código tiene y cuánto tiempo escribir. Aquí está el código en 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); 
     } 
    } 
} 

He hecho la mayor parte de las clases e interfaces relacionadas clases internas para mantener el ejemplo compacto y pasteable para la compilación inmediata. En una implementación real, serían clases regulares de alto nivel. La aplicación de línea de comandos imprime las ventanas visibles y su posición. Lo ejecuté tanto en jvm de 32 bits como en 64 bits, y obtuve los mismos resultados para cada uno.

EDIT2: Código actualizado para incluir z-order. Utiliza GetNextWindow.En una aplicación de producción, probablemente debería llamar a GetNextWindow dos veces para los valores siguientes y anteriores y verificar que sean coherentes y que sean identificadores de ventanas válidos.

+0

para estar seguro, si simplemente sondeo puedo usar EnumWindows y no hay problemas de 32-/64-bit? JNA era el plan (tenían un ejemplo enumerando las ventanas en Windows por cierto, solo necesito encontrarlo de nuevo) pero primero quería saber qué estaba involucrado "debajo del capó". Todavía no sé si tendré las habilidades para hacerlo yo mismo (nunca he hecho ninguna programación de Windows), pero todas estas respuestas me ayudan mucho a comprender lo que está involucrado. – NoozNooz42

+0

@ NoozNooz42 - Eso es correcto, el sondeo evita el uso de ganchos y el problema de 32/64 bits. Aquí hay un artículo que llama a EnumWindows desde JNA. http://javajeff.mb.ca/cgi-bin/mp.cgi?/java/javase/articles/mjnae – mdma

+0

@mdna: El problema con EnumWindows es que no devuelve las ventanas en orden Z. (O al menos no según su documentación) –

7

se puede hacer esto?

Sí, aunque tendría que registrar un hook para obtener lo que desea con respecto a una devolución de llamada. Es probable que necesite usar un CBTProc Callback Hook, que se llama siempre que:

de activación, crear, destruir, minimizar, maximizar, mover o dimensionamiento de una ventana; antes de completar un comando del sistema; antes de eliminar un evento de mouse o teclado de la cola de mensajes del sistema; antes de configurar el foco del teclado; o antes de sincronizar con la cola de mensajes del sistema

Sin embargo, tenga en cuenta que no creo que dichos enganches funcionen en las ventanas de la Consola porque son el dominio del kernel, no de Win32.

¿hay API Windows bien documentadas (y funcionando de acuerdo con sus especificaciones) que permiten hacer eso?

Sí. Puede usar las funciones GetTopWindow y GetNextWindow para obtener todos los identificadores de ventana en el escritorio en el orden Z correcto.

¿Es fácil registrar una devolución de llamada cada vez que cambia una ventana? (Si se cambia el tamaño, mover, llevado a delante/detrás o si aparece una nueva ventana en marcha, etc.)

Ver primera respuesta :)

cuáles serían las trampas ser?

Ver primera respuesta :)

Bono pregunta: imagina que había necesidad de escribir una pequeña .exe escribir los nombres de las ventanas/posición/tamaño a un archivo temporal cada vez que hay un cambio de ventana en la pantalla , ¿cuánto tiempo duraría ese programa aproximadamente en el idioma de su elección y por cuánto tiempo necesitaría escribirlo?

Cientos de líneas de C, y un par de horas. Aunque tendré que usar alguna forma de sondeo, nunca antes he hecho anzuelos. Si necesito los ganchos me tomaría algo más de tiempo.

+0

muchas gracias por esta respuesta ... Así que si te entiendo correctamente o estarás sondeando (eso es lo que hago bajo OS X) y luego sin enganches y sin devolución de llamada ** O ** estarías usando ganchos, permitiendo tener una devolución de llamada? (una vez más, en OS X AFAICT no hay una manera fácil de hacer esto usando una devolución de llamada, que es la razón por la que estoy sondeando). – NoozNooz42

+1

@Nooz: Un gancho * es * una devolución de llamada. –

+1

@Nooz: Dicho de otra manera, sí. Puede sondear o puede usar el gancho como devolución de llamada. El anzuelo es mejor, pero solo estaba calculando el tiempo que tomaría ya que nunca antes había usado anzuelos. –

1

Recuerdo que en 2006 había una utilidad WinObj como parte de sysinternals que posiblemente hizo lo que quería. Parte de estas utilidades fueron provistas con el código fuente por autor (Mark Russinovich).

Desde entonces, su empresa fue comprada por Microsoft, por lo que no sé si la fuente seguirá estando disponible.

también lo siguiente puede ser digno de la comprobación:

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

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

Cuestiones relacionadas