2010-07-06 44 views
39

(所有的意思是重新標記與相關技術:我不知道他們是哪些:)Windows:如何獲取所有可見窗口的列表?

我可能會用更詳細的問題,有關具體細節稍後但現在我試圖掌握「全局圖」:我正在尋找一種方法來枚舉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) 

這時如果用戶(或OS)將窗口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下(我真的這麼做了,這是毫無疑問)和我有幾個問題:

  • 才能做到這一點?

  • 是否有良好的記錄Windows API(並按照其規範工作)允許這樣做?

  • 每次窗口變化時,是否容易註冊回調? (如果它被調整大小,移動,回到前面或者如果彈出一個新窗口等)

  • 這個問題會是什麼?

我知道這個問題是不特定的,這就是爲什麼我試圖儘可能清楚地描述了我的問題(包括漂亮的ASCII藝術的,你可以給予好評此):現在我在看「大圖」。我想知道在Windows下做什麼這樣的事情。

獎金的問題:假設你需要寫一個小的.exe寫窗口名稱/位置/大小到一個臨時文件每次有上畫面窗口的變化,多長時間會在這樣一個方案是你的左右選擇的語言和你需要寫多久?

(再一次,我試圖讓「大畫面」明白什麼是在這裏工作)

+0

沒有人?已經+1 +1和1最喜歡... :) – NoozNooz42 2010-07-06 23:29:18

+0

嗯,我想你會開始與FindWindowEx枚舉Z順序的所有窗口,然後使用GetWindowLong獲取窗口樣式。顯然,你只會考慮帶有WS_VISIBLE的窗口,也許可以使用WS_CAPTION或其他。 – Luke 2010-07-07 00:57:13

+0

.net框架是一個選項嗎?我有一個簡單的解決方案,需要十秒。 – Cyclone 2010-07-08 19:14:49

回答

23

枚舉頂層窗口,你應該使用EnumWindows而不是GetTopWindow/GetNextWindow,因爲EnumWindows的返回一個一致的看法的窗口狀態。當窗口在迭代過程中改變z順序時,您可能會使用GetTopWindow/GetNextWindow獲取不一致的信息(例如報告刪除的窗口)或無限循環。

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必須具有不同的 名稱。 (從調用SetWindowsHookEx API頁面)

正如你在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) {  // 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 2010-07-13 16:06:22

+0

@ NoozNooz42 - 這是正確的,輪詢避免使用鉤子和32/64位的問題。這裏有一篇從JNA調用EnumWindows的文章。 http://javajeff.mb.ca/cgi-bin/mp.cgi?/java/javase/articles/mjnae – mdma 2010-07-13 16:30:14

+0

@mdna:EnumWindows的問題是,它不會以Z順序返回窗口。 (或者至少不會根據它的文檔) – 2010-07-13 17:12:39

7

才能做到這一點?

是的,儘管您必須註冊hook才能獲得想要的回調。你可能會需要使用CBTProc Callback Hook,被稱爲每當:

激活,創建,銷燬,最小化,最大化,移動或大小的窗口;在完成系統命令之前;在從系統消息隊列中刪除鼠標或鍵盤事件之前;在設置鍵盤焦點之前;或系統消息隊列

注意同步之前但是,我不相信這樣的掛鉤控制檯窗口工作,因爲他們是內核,而不是Win32的域。

是否有良好的記錄Windows API(並按照其規範工作)允許這樣做?

是的。您可以使用GetTopWindowGetNextWindow函數以正確的Z順序獲取桌面上的所有窗口句柄。

每次窗口變化時,是否容易註冊回調? (如果它被調整,移動,帶來正面/背面,或者彈出一個新窗口式等)

見第一個答案:)

會是什麼陷阱呢?

見第一個答案:)

獎金的問題:假設你需要寫一個小的.exe寫窗口名稱/位置/大小到一個臨時文件每次有上畫面窗口的變化,您的選擇語言大約需要多長時間?您需要寫多久?

幾百行C和幾個小時。雖然我不得不使用某種形式的投票 - 我從未在自己之前做過鉤。如果我需要掛鉤,它需要更長的時間。

+0

非常感謝這個答案...所以,如果我正確地理解你,你會投票(這就是我在OS X下做的),然後沒有鉤子和沒有回調**或**你會使用鉤子,允許有回調? (再一次,在OS X AFAICT中,使用回調函數沒有簡單的方法,這就是爲什麼我要輪詢)。 – NoozNooz42 2010-07-08 19:49:14

+1

@Nooz:一個鉤子*是一個回調。 – 2010-07-08 20:32:13

+1

@Nooz:換一種方式,是的。您可以輪詢,也可以使用鉤子作爲回調。鉤子更好,但我只是估計它會花費的時間,因爲我以前從未使用鉤子。 – 2010-07-08 20:40:12

相關問題