2017-06-12 63 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,如果都没有找到。
  • 在回调函数中存储的顶层手柄相比,发现窗口的顶层句柄,如果他们匹配,窗口消息被发送到发现窗口的句柄