2010-02-25 222 views
2

我有一個運行三個應用程序的Windows機器。當應用程序啓動時,每個應用程序創建一個無邊界窗口,其定位使得它們以特定方式重疊。窗口處理管理器

目前,當我點擊底部窗口上的一個控件時,它會進入窗口堆棧的頂部。

我需要確保每個窗口在窗口堆棧中保持其順序,即使窗口接收到輸入。

我在想我需要寫一些簡單的窗口管理器來維持正確的窗口順序。

問題是,我需要知道一個特定窗口是否改變了位置。我發現有一個WM_WINDOWPOSCHANGING消息,但我的理解是,此消息發送到位置已更改的窗口。

我需要我的窗口管理器應用程序以某種方式通知Z順序已經改變。

是否有某種方法可以捕獲所有WM_消息並確定消息是否適用於我希望控制的窗口之一?

回答

2

而不是MSalter的方法試圖將DLL注入到每個運行應用程序,請考慮安裝WH_CBT Windows掛鉤。在你的CBTProc中,當你得到你關心的三個應用程序窗口句柄的HCBT_MOVESIZE時返回0。

閱讀MSDN中關於CBTProcSetWindowsHookEx上的文檔。

0

您可以使用SetWindowPos以您想要的Z順序定位窗口。我建議你攔截WM_FOCUS消息(這個時發送到接收焦點的窗口)

在你的WndProc功能,也許你可以嘗試這樣的事:

LRESULT wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){ 
    // other stuff.. 

    switch (msg){ 
     case WM_FOCUS: 
     { 
      HWND firstWindow; // get the first window here 
      HWND secondWindow; // this would be the second window 
      HWND thirdWindow; // this would be the third window 
      // TODO: initialize the windows correctly, based on your priority 
      SetWindowPos(firstWindow, secondWindow, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); // position the second window below the first window 
      SetWindowPos(secondWindow, thirdWindow, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); // position the third window below the second window 
     } 
     return 0; 
    } 
    // other messages.. 
} 

我不太確定SetWindowPos參數的排序順序,因爲我現在無法測試代碼,但也許這可以讓你去?


如果需要攔截所有WM_消息,我會建議調用CreateWindowEx自己的窗口類的應用程序調用,而不是(我猜)。例如:

class Window { 
public 
    Window(){ 
     ... 
     WNDCLASSEX wc; 
     ZeroMemory(&wc, sizeof(WNDCLASSEX)); 
     wc.cbSize  = sizeof(WNDCLASSEX); 
     wc.lpfnWndProc = wndProc;   // <- Note this one 
     ... 
    } 

    static LRESULT WINAPI wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){ 
     // reference: http://www.gamedev.net/community/forums/topic.asp?topic_id=303854 - Evil Steve [Moderator] 
     Window* parent; 

     // Get pointer to window 
     if(msg == WM_CREATE){ 
      parent = (Window*)((LPCREATESTRUCT)lParam)->lpCreateParams; 
      SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)parent); 
     } 
     else{ 
      parent = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA); 
      if(!parent) return DefWindowProc(hwnd,msg,wParam,lParam); 
     } 
     HWND prev = parent->mWin; 
     parent->mWin = hwnd; 
     LRESULT ret = parent->wndProc(msg,wParam,lParam); 
     parent->mWin = prev; 
     return ret; 
    } 

    virtual LRESULT wndProc(UINT msg, WPARAM wParam, LPARAM lParam){ 
    } 
}; 

在這個例子中你的應用程序會從窗口繼承,基本上提供了一個稍微修改的WndProc功能(這將是缺少HWND所以這將需要存儲在某個地方,除非你選擇它向上用戶數據)。

  • 每當您收到消息時,Window::wndProc(HWND, UINT, WPARAM, LPARAM)函數都會檢測到它。在這裏,您可以對任何消息進行檢查,包括(但不限於)WM_WINDOWPOSCHANGING

  • 其他的事情將是:
    wndProc(UINT, WPARAM, LPARAM),而不是調用DefWindowProc(..)
    你打電話Window::wndProc(UINT, WPARAM, LPARAM)。然後,你可以做你的支票在那裏,而不是(如不cludge第一wndProc功能):)

這個的缺點是,如果應用程序是別人寫的,他們需要遵守你的windowclass。正如你解釋的那樣,用戶不需要與窗口管理器交互,但是,採用這種方法,唯一的交互作用就是讓窗口管理器爲用戶創建窗口。
否則我認爲你將不得不去解決在其他答案中解釋的鉤子

+0

我寧願它,如果用戶不應該被要求與'窗口管理器'應用程序進行交互。 我希望能有一些方法來檢測窗口順序何時改變,然後讓'窗口管理器'應用程序自動響應。例如,使用kludgey的方法是設置一個計時器,以便'窗口管理器'應用程序每隔X個時間段檢查窗口順序並在必要時重新排序。 我希望能有一些其他的消息可以用來確定何時檢查重新排序是否必要。 – JRT 2010-02-25 13:30:14

0

最簡單的方法可能是將DLL注入到三個應用程序中的每一箇中。這確保您只需處理您實際關心的窗口消息的子集。

接下來,通過調用EnumWindows()來查找每個應用程序中的主窗口(理論上可能還有更多),並在每個窗口中調用GetWindowThreadProcessId()以確定它是否屬於當前進程(即一個你的DLL被注入)。

既然你有正確的HWND,你可以鉤住關聯的WndProc並捕獲發送給它的任何WM_WINDOWPOSCHANGING。

0

當您創建要置頂的兩個窗口時,請將您希望置於最下面的窗口設爲hWndParent的值爲CreateWindow。當底部窗口向前移動時,Windows將始終將這些窗口向前移動,以便它們始終保持在它的前面。

因此,如果您的底部窗口是窗口1.首先創建它,然後當您創建窗口2時,窗口1的句柄作爲hWndParent值。窗口管理員完成剩下的工作。

0

我想我會同意John Knoeller的回答。如果您希望窗口保持特定的z順序,請確定此順序,並使用適當的父子關係創建窗口。

::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent); 

當你這樣做時,子窗口將始終停留在父窗口之上。

如果你仍然堅持捕獲消息,你可以嘗試在每個窗口中捕獲WM_ACTIVATE,然後將該消息轉發給窗口管理器,窗口管理器可以訪問所有窗口的窗口並使用SetWindowPos正確命令它們。而不是SetWindowPos,您可以使用DeferWindowPos一次爲窗口更改z順序並避免閃爍。