2017-06-12 60 views
3

在C++程序(embarcadero XE2,vcl)中,我想從父窗口向所有子窗口發送窗口消息。 爲此,我註冊了一個windowMessage,發送消息的PostMessage(handle,msg,wparam,lparam)在所有句柄的循環中,並在每個對話框上用WndProc(TMessage& Message)接收。跟蹤打開的子對話框

我的問題是跟蹤打開的窗口句柄。由於大多數對話框都是通過Show()打開的,所以多個對話框可以同時運行。

到目前爲止,我用std::vector<HWND>來存儲窗口句柄。但是,這需要我跟蹤哪個句柄一次仍然有效。 我可以通過添加一個onClose處理程序的對話解決這個問題,並在對話框的句柄作爲參數的主線程調用一個過程,所以它可以從載體上移除...

是否有更好的解決方案,就像Application.OpenForms(.NET)中的自更新列表一樣?或者,也許更好的方法來通知主對話框中的事件的子對話框?

+1

[在Windows中,對話框是不是*兒童* ,他們是*擁有的Windows *。](https://blogs.msdn.microsoft.com/oldnewthing/20100315-00/?p=14613) – andlabs

+0

這似乎是一個可怕的使用註冊窗口消息,這是最好的使用當來自不同供應商的應用程序試圖通信時 - 可能通過廣播消息。使用WM_USER或WM_APP定義的範圍可以更好地發送自己的Windows消息。 –

+0

@andlabs感謝您的評論!如果你沒有提到兒童與自有對話之間的區別,我將無法弄清楚爲什麼科迪的解決方案不起作用。 ^^ – Julian

回答

3

一個窗口已經在內部跟蹤它的孩子,所以你只需要點擊它。如果你想發送消息給窗口的所有子窗口,那麼你只需遞歸遍歷那個窗口的所有子窗口,並將消息發送給每個窗口。

起點爲GetTopWindow function,它返回Z順序頂部的子窗口。然後,通過調用GetNextWindow function來遍歷子窗口。

MFC實際上包含一個這樣做的方法,稱爲SendMessageToDescendants。您可以自己編寫相應的代碼,並使用PostMessage代替SendMessage,如果您更願意使用這些語義。

void PostMessageToDescendants(HWND hwndParent, 
           UINT uMsg, 
           WPARAM wParam, 
           LPARAM lParam, 
           BOOL bRecursive) 
{ 
    // Walk through all child windows of the specified parent. 
    for (HWND hwndChild = GetTopWindow(hwndParent); 
     hwndChild  != NULL; 
     hwndChild  = GetNextWindow(hwndChild, GW_HWNDNEXT)) 
    { 
     // Post the message to this window. 
     PostMessage(hwndChild, uMsg, wParam, lParam); 

     // Then, if necessary, call this function recursively to post the message 
     // to all levels of descendant windows. 
     if (bRecursive && (GetTopWindow(hwndChild) != NULL)) 
     { 
     PostMessageToDescendants(hwndChild, uMsg, wParam, lParam, bRecursive); 
     } 
    } 
} 

的參數是一樣的PostMessage功能,除了最後一個:bRecursive。這個參數意味着它的名字所暗示的。如果TRUE,子窗口的搜索將是遞歸,以便該消息將被髮布到父窗口的所有後代(其子代,其子代的子代等)。如果FALSE,郵件將只發布給其直接子女。

+0

感謝您快速解釋的答案! 'GetNextWindow(hwndChild,GW_HWNDNEXT))'似乎沒有編譯,但'GetWindow(hwndChild,GW_HWNDNEXT)'似乎做了預期的......在我使用owlnext作爲主應用程序MDI窗口之前,我沒有提到由於歷史原因)和vcl的孩子形式。孩子在構造函數中有三行被打開:'tf = new TForm(Owner-> Handle); tf-> ParentWindow = Owner-> Handle; this-> PopupParent = tf; '用你的代碼,'WndProc'不再收到消息......也許它被卡在'TForm * tf'? – Julian

+0

此答案實際上不會將消息發送到擁有的對話框。但是,我不確定這是如何解決的......我們是否需要枚舉所有頂級窗口並過濾掉那些不屬於有問題的窗口的窗口?以這種方式進行遞歸搜索需要更長的時間,因爲每次都必須重新讀取整個列表:|那麼MSDN製作的最高層和頂層窗口之間的區別呢? – andlabs

+0

問題是關於子窗口,@andlabs,關於*擁有的窗口。 –

0

Cody Gray爲我提出的問題提供了正確的解決方案。

但是,如評論所示,我問了一個錯誤的問題。

我的對話是VCL這是由貓頭鷹TWindow開業,這意味着我用對話的ParentWindow屬性來獲取模態外觀TForms

__fastcall TMyDialog::MyDialog(owl::TWindow* Owner) :TForm(HWND(NULL)){ 
    tf = new TForm(Owner->Handle); //TForm*, Mainwindow calls this dialog 
    tf->ParentWindow = Owner->Handle; // workaround: owl calls vcl 
    this->PopupParent = tf; // workaround: owl calls vcl 
} 

那麼,其結果很可能孩子們的混合和擁有對話。

什麼工作對我來說讓窗口消息從主窗口被打開,所有的對話是這樣的:

struct checkMSG{ 
    HWND handle; UINT msg; WPARAM wparam; LPARAM lparam; 
}; 
checkMSG msgCheck; 

BOOL CALLBACK enumWindowsProc(__in HWND hWnd,__in LPARAM lParam) { 
    HWND owner= ::GetParent(hWnd); 
    if(owner==msgCheck.handle) 
     ::PostMessage(hWnd, msgCheck.msg, msgCheck.wparam, msgCheck.lparam); 
    return TRUE; 
} 

void SendToAllWindows(HWND handle, UINT msg, WPARAM wparam, LPARAM lparam){ 
    msgCheck.handle=::GetParent(handle); 
    msgCheck.msg=msg; 
    msgCheck.wparam= wparam; 
    msgCheck.lparam= lparam; 
    BOOL enumeratingWindowsSucceeded = ::EnumWindows(enumWindowsProc, NULL); 
} 
  • EnumWindows的迭代有關的所有應用程序的所有窗口。
  • 爲了確保只獲取我自己的應用程序的窗口句柄,我使用變量msgCheck來存儲頂級句柄和我想發送的消息。
  • 現在我使用GetParent檢索所有者或父或返回NULL,如果都沒有找到。
  • 在回調函數中存儲的頂層手柄相比,發現窗口的頂層句柄,如果他們匹配,窗口消息被髮送到發現窗口的句柄