2010-07-06 14 views
39

(da parte di tutti significa fare ri-tag con la tecnologia in questione: Non so quali sono :)Windows: come ottenere un elenco di tutte le finestre visibili?

Io probabilmente venire più tardi con domande più dettagliate, circa i dettagli specifici, ma per ora mi Sto cercando di cogliere il "quadro generale": sto cercando un modo per enumerare "finestre reali visibili" su Windows. Con "finestra reale visibile" intendo proprio questo: ciò che un utente chiamerebbe una "finestra". Ho bisogno di un modo per ottenere un elenco di tutte queste finestre visibili, in ordine Z.

Si noti che I do ha davvero bisogno di farlo. L'ho già fatto su OS X (dove è un vero mal di testa da fare, specialmente se vuoi supportare OS X 10.4, perché OS X non ha una comoda API di Windows) e ora ho bisogno di farlo sotto Windows.

Ecco un esempio, supponiamo che ci sono tre finestre visibili sullo schermo, in questo modo:

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

poi ho bisogno di tornare una lista come questa:

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

Poi, se il l'utente (o il sistema operativo) porta la finestra A in primo piano, diventa:

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

E I ottenere (idealmente) un callback dandomi questo:

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

Facendo questo sotto OS X richiede l'uso di hack incredibilmente strane (come mandato l'utente di attivare "Abilita l'accesso a dispositivi assistive"!), ma ho L'ho fatto sotto OS X e funziona (sotto OS X, non sono riuscito a ottenere una richiamata ogni volta che si verificano alcune modifiche alle finestre, quindi sto eseguendo il polling, ma ho avuto modo di funzionare).

Ora io voglio fare questo sotto Windows (davvero, non c'è dubbio su di esso) e ho un paio di domande:

  • si può fare?

  • ci sono API Windows ben documentate (e funzionanti secondo le loro specifiche) che consentono di farlo?

  • è facile registrare una richiamata ogni volta che cambia una finestra? (se viene ridimensionato, spostato, riportato in primo piano o se viene visualizzata una nuova finestra, ecc.)

  • cosa sarebbero i trucchi?

So che questa domanda non è specifico, ed è per questo che ho cercato di descrivere il mio problema più chiaramente possibile (compreso piacevole arte ASCII per il quale è possibile upvote questo): per ora sto guardando la grande immagine". Voglio sapere che cosa comporta una cosa del genere in Windows.

domanda

Bonus: immaginate avresti bisogno di scrivere un piccolo exe ​​ scrivendo le finestre nomi/posizione/dimensione per un file temporaneo ogni volta c'è un cambiamento finestra sullo schermo, quanto a lungo sarebbe un programma del genere essere approssimativamente nella vostra lingua di scelta e per quanto tempo avresti bisogno di scriverlo?

(ancora una volta, sto cercando di ottenere il "grande quadro" per capire che cosa è al lavoro qui)

+0

nessuno? Già +1 upvote e 1 preferito ... :) – NoozNooz42

+0

Bene, immagino che inizieresti con FindWindowEx per enumerare tutte le finestre nell'ordine Z e quindi utilizzare GetWindowLong per ottenere lo stile della finestra. Ovviamente considereresti solo Windows con WS_VISIBLE e forse WS_CAPTION o qualcosa del genere. – Luke

+0

Is .net framework un'opzione? Ho una soluzione semplice per questo, dura circa dieci secondi. – Cyclone

risposta

23

per enumerare le finestre di primo livello, si dovrebbe usare EnumWindows piuttosto che GetTopWindow/GetNextWindow, dal momento che EnumWindows restituisce una visione coerente dello stato della finestra. Si rischia di ottenere informazioni incoerenti (come report su finestre eliminate) o loop infiniti usando GetTopWindow/GetNextWindow, quando windows modifica l'ordine z durante l'iterazione.

EnumWindows utilizza una richiamata. Ad ogni chiamata del callback si ottiene un handle di finestra. Le coordinate dello schermo della finestra possono essere recuperate passando tale maniglia a GetWindowRect. La richiamata crea un elenco delle posizioni della finestra nell'ordine z.

È possibile utilizzare il polling e creare ripetutamente l'elenco delle finestre. Oppure, si imposta un CBTHook per ricevere notifiche delle modifiche alle finestre. Non tutte le notifiche CBT comporteranno modifiche all'ordine, alla posizione o alla visibilità delle finestre di livello superiore, quindi è consigliabile rieseguire EnmWindows per creare un nuovo elenco di posizioni della finestra nell'ordine z e confrontarlo con l'elenco precedente prima di elaborare ulteriormente l'elenco, in modo che l'ulteriore elaborazione avvenga solo quando si è verificato un vero cambiamento.

Si noti che con l'aggancio non è possibile mescolare 32 e 64 bit. Se stai utilizzando un'applicazione a 32 bit, riceverai le notifiche dai processi a 32 bit. Allo stesso modo per 64-bit. Pertanto, se si desidera monitorare l'intero sistema su una macchina a 64 bit, sembrerebbe necessario eseguire due app. Il mio ragionamento deriva dalla lettura di questo:

SetWindowsHookEx può essere usato per iniettare una DLL in un altro processo. Una DLL a 32 bit non può essere iniettata in un processo a 64 bit e una DLL a 64 bit non può essere immessa in un processo a 32 bit. . Se un'applicazione richiede l'uso di ami in altri processi, è richiesto che un'applicazione chiamata 32 bit SetWindowsHookEx per iniettare un 32 bit DLL in processi a 32 bit, e una chiamata un'applicazione 64 bit SetWindowsHookEx per iniettare una DLL a 64 bit in processi a 64 bit. Le DLL a 32 bit e 64 bit devono avere nomi diversi . (. Dalla pagina api SetWindowsHookEx)

Come stai attuazione del presente in Java, si potrebbe desiderare di guardare JNA - rende la scrittura l'accesso alle librerie native molto più semplice (chiamando il codice in Java) e rimuove il bisogno della tua DLL JNI nativa.

MODIFICA: hai chiesto quanto codice è e quanto a lungo scrivere. Ecco il codice 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) {  // 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); 
     } 
    } 
} 

ho fatto la maggior parte delle relative classi e interfacce classi interne per mantenere l'esempio compatto e pasteable per la compilazione immediata. In un'implementazione reale, sarebbero normali classi di livello superiore. L'app della riga di comando stampa le finestre visibili e la loro posizione. L'ho eseguito sia su jvm a 32 bit che a 64 bit e ho ottenuto gli stessi risultati per ciascuno.

EDIT2: codice aggiornato per includere z-order. Utilizza GetNextWindow.In un'applicazione di produzione, è consigliabile chiamare GetNextWindow due volte per i valori successivo e precedente e verificare che siano coerenti e che siano handle di finestra validi.

+0

solo per essere sicuro, se scrivo semplicemente, posso semplicemente usare EnumWindows e non c'è un problema 32-/64-bit? JNA era il piano (avevano un esempio che enumerava Windows su Windows, ho solo bisogno di ritrovarlo) ma prima volevo sapere cosa fosse coinvolto "sotto il cofano". Non so ancora se avrò le capacità per farlo da solo (non ho mai fatto nessuna programmazione Windows) ma tutte queste risposte mi stanno aiutando molto a capire cosa comporta! – NoozNooz42

+0

@ NoozNooz42 - Corretto, il polling evita l'uso di hook e il problema a 32/64 bit. Ecco un articolo che chiama EnumWindows di JNA. http://javajeff.mb.ca/cgi-bin/mp.cgi?/java/javase/articles/mjnae – mdma

+0

@mdna: Il problema con EnumWindows è che non restituisce le finestre nell'ordine Z. (O almeno non secondo la sua documentazione) –

7

si può fare?

Sì, anche se è necessario registrare uno hook per ottenere ciò che si desidera per quanto riguarda una richiamata. Si sarebbe probabilmente necessario utilizzare un CBTProc Callback Hook, ogni volta che si chiama:

attivazione, creando, distruggendo, riducendo al minimo, massimizzando, lo spostamento o ridimensionamento di una finestra; prima di completare un comando di sistema; prima di rimuovere un evento del mouse o della tastiera dalla coda dei messaggi di sistema; prima di impostare il fuoco della tastiera; o prima di sincronizzarsi con la coda messaggi di sistema

Nota comunque che non credo che tali hook funzionino su Console windows perché sono il dominio del kernel, non Win32.

ci sono API Windows ben documentate (e funzionanti secondo le loro specifiche) che consentono di farlo?

Sì. È possibile utilizzare le funzioni GetTopWindow e GetNextWindow per ottenere tutti gli handle di finestra sul desktop nell'ordine Z corretto.

è facile registrare una richiamata ogni volta che cambia una finestra? (Se viene ridimensionato, spostato, portato a fronte/retro o se una nuova finestra si apre-up, ecc)

Vedi prima risposta :)

quali sarebbero i trucchi essere?

Vedi prima risposta :)

Domanda bonus: immaginate avresti bisogno di scrivere un piccolo exe scrivendo le finestre nomi/posizione/dimensione per un file temporaneo ogni volta c'è un cambiamento finestra sullo schermo , per quanto tempo un programma del genere dovrebbe essere approssimativamente nella tua lingua di scelta e per quanto tempo avresti bisogno di scriverlo?

Alcune centinaia di righe di C e un paio d'ore. Anche se dovrei usare qualche forma di sondaggio - non ho mai fatto ami prima di me stesso. Se avessi bisogno dei ganci ci vorrebbe un po 'di più.

+0

grazie mille per questa risposta ... Quindi se ti capisco correttamente o staresti facendo il polling (questo è quello che faccio sotto OS X) e poi nessun hook e nessuna callback ** O ** useresti ganci, permettendo di avere una richiamata? (ancora una volta, in OS X AFAICT non esiste un modo semplice per farlo utilizzando una richiamata, motivo per cui sto effettuando il polling). – NoozNooz42

+1

@Nooz: un hook * è * un callback. –

+1

@Nooz: In altre parole, sì. È possibile eseguire il polling oppure è possibile utilizzare l'hook come callback. L'aggancio è migliore ma stavo solo stimando il tempo necessario perché non ho mai usato gli uncini prima. –

1

Ricordo che nel 2006 c'era un'utilità WinObj come parte di sysinternals che probabilmente faceva ciò che si desiderava. Una parte di queste utility è stata fornita con il codice sorgente dall'autore (Mark Russinovich).

Da quel momento, la sua azienda è stata acquistata da Microsoft, quindi non so se la fonte sarebbe ancora disponibile.

anche i seguenti può essere la pena di verificare:

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

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

Problemi correlati