Message Management

Sending messages to views

MFC has a wonderful message routing mechanism. A command message first comes to the active view, then the view frame, then the document, and so on. This works for WM_COMMAND and WM_UPDATE_COMMAND_UI messages, but does not work for any other kind of message, including user-defined messages. This is a real pain, and somewhat silly because it would be easy for Microsoft to implement. So a problem arises when you need to send a message to some sub-window, but you can't send a WM_COMMAND message because you are not directly responding to a GUI component.

Of course, you could invent a fictitious control and generate your own imitation WM_COMMAND messages. But this is very risky, and contributes to significant future unmaintainability.

What can you do?

I've done several things. Sometimes, I just put a message handler in the child frame whose sole purpose is to route the message to the child window contained in the frame. This can be a pain, but gives you serious control over how the messages are processed. Thus, the main frame, to which I post a message (often from a thread), has a handler of the form

// ... in the message map
ON_REGISTERED_MESSAGE(UWM_THREAD_DID_SOMETHING, OnThreadDidSomething)
 
// ... the handler
LRESULT CMainFrame::OnThreadDidSomething(WPARAM wParam, LPARAM lParam)
    {
     CView * active = GetActiveView();
     if(active != NULL)
        active->SendMessage(UWM_THREAD_DID_SOMETHING, 
                            wParam, lParam);
     return 0;
    }

Sometimes you have to send the message not to the view, but to its parent frame window. This is done by the following code:

RESULT CMainFrame::OnThreadDidSomething(WPARAM wParam, LPARAM lParam)
    {
     CFrameWnd * active = GetActiveFrame();
     if(active != this)
        active->SendMessage(UWM_THREAD_DID_SOMETHING, 
                            wParam, lParam);
     return 0;
    }

Note the special test above. If, for some reason, you had managed to kill off all the MDI children while the thread was still running, GetActiveFrame returns this, which would mean you would get into a semi-infinite SendMessage loop which would terminate when you ran out of stack space. Note that because the function is defined as returning this we don't have to worry about the possibility that a temporary window handle has been returned, a caution I discuss in my essay on the use of Attach/Detach. This requires that you use the CMDIChildFrame subclass, and introduce a similar handler there:

BEGIN_MESSAGE_MAP(CMyMDIChildFrame, CMDIChildWnd)
     ON_REGISTERED_MESSAGE(UWM_THREAD_DID_SOMETHING, OnThreadDidSomething)
 
// and the handler is
LRESULT CMyMDIChildFrame::OnThreadDidSomething(WPARAM wParam, LPARAM lParam)
    {
     // ... do things to the frame window (resize, reposition?)
     // and if appropriate, pass the message down...
     SendMessageToDescendants(UWM_THREAD_DID_SOMETHING, 
                              wParam, lParam, 
                              FALSE, // only go one level deep
                              TRUE); // only to permanent windows
     return 0;
    }

Now, you may wonder why I didn't send it directly to the child window. Partly because I'm lazy, and SendMessageToDescendants does what I need. Partly because I don't need the result, because it came in from PostMessage. In a case where I had to route a message to the descendant where I needed the result, I'd instead write

CView * view = (CView *)GetWindow(GW_CHILD);
ASSERT(view != NULL && view->IsKindOf(RUNTIME_CLASS(CView)));
view->SendMessage(...);

Summary

User-defined messages are a powerful and flexible mechanism for handling the passing of information and control between levels of your application, threads of your application, and processes. However, using them effectively requires certain degrees of care. I favor using ::RegisterWindowMessage and GUIDs to define all messages. Interthread and interprocess sends are risky, and should be avoided; if you must, use SendMessageTimeout to ensure that you will not end up in deadlock.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“There are 10 types of people in the world, those who can read binary, and those who can't.”