Keeping track of open child dialogs

1.3k Views Asked by At

In a C++ program (embarcadero XE2, vcl) I would like to send window-messages from parent to all child windows. For this, I registered a windowMessage, send the message with PostMessage(handle,msg,wparam,lparam) in a loop for all handles and receive it on each dialog with WndProc(TMessage& Message).

My problem is to keep track of the open window handles. Since most dialogs are opened via Show(), multiple of them can run at the same time.

So far I used a std::vector<HWND> to store the Window-Handles. However, this would require me to keep track of which handle is still valid at a time. I could solve this by adding a onClose Handler to the dialogs and call a procedure in the main thread with the dialog's handle as a parameter, so it can be removed from the vector...

Is there a nicer solution, like a self-updating list as in Application.OpenForms (.NET)? Or maybe a better way to notify child-dialog of an event from the main dialog?

2

There are 2 best solutions below

6
Cody Gray - on strike On BEST ANSWER

A window already tracks its children internally, so you just need to tap into that. If you want to send a message to all of a window's child windows, then you just need to recursively iterate through all of that window's children, sending the message to each one.

The starting point is the GetTopWindow function, which returns the child window at the top of the Z order. Then, you iterate through the child windows by calling the GetNextWindow function.

MFC actually includes a method that does this, called SendMessageToDescendants. You can write the equivalent yourself, and replace SendMessage with PostMessage, if you'd prefer those semantics.

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);
      }
   }
}

The arguments are the same as the PostMessage function, except for the last one: bRecursive. This parameter means just what its name suggests. If TRUE, the search of child windows will be recursive, so that the message will be posted to all descendants of the parent window (its children, its children's children, etc.). If FALSE, the message will be posted only to its immediate children.

0
Julian On

Cody Gray has given the right solution to the question I asked.

However, as shown in the comments, I asked the wrong question.

My dialogs were vcl TForms which were opened by Owl TWindow, which means I use the ParentWindow property of the dialogs to get a modal appearance:

__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
}

So, the result is likely a mix of children and owned dialogs.

What worked for me to get Window Messages to all dialogs that were opened from the main window was this:

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 iterates about all windows in all applications.
  • To be sure to only get my own application's window's handles, I used a variable msgCheck to store the toplevel handle and the message I want to send.
  • Now I use GetParent which retrieves the Owner or the Parent or returns NULL if neither is found.
  • In the callback function the stored toplevel handle is compared to the found window's toplevel handle and if they match, the window message is send to the found window's handle