Search code examples
c++winapiuser-interfacepostmessage

PostMessage() succeeds but my message processing code never receives the message


In my C++ application's GUI object I have the following in the main window procedure:

case WM_SIZE:
    {
        OutputDebugString(L"WM_SIZE received.\n");
        RECT rect = {0};
        GetWindowRect(hwnd, &rect);
        if (!PostMessage(0, GUI_MSG_SIZECHANGED, w, MAKELONG(rect.bottom - rect.top, rect.right - rect.left))) {
            OutputDebugString(L"PostMessage failed.\n"); // <--- never called
        }
    }

    return 0; // break;

The GUI object also has the following getMessage() method:

int GUI::getMessage(MSG & msg) {
    BOOL result = 0;

    while ((result = GetMessage(&msg, 0, 0, 0)) > 0) {
        if (msg.message > (GUI_MSG_BASE-1) && msg.message < (GUI_MSG_LAST+1)) {
            OutputDebugString(L"GUI message received.\n");
            break;
        }
        else {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return result;
}

The application object calls this method in the following way:

while ((result = _gui.getMessage(msg)) > 0) {
    switch (msg.message) {
        // TODO: Add gui message handlers
        case GUI_MSG_SIZECHANGED:
            OutputDebugString(L"GUI_MSG_SIZECHANGED received.\n");
            _cfg.setWndWidth(HIWORD(msg.lParam));
            _cfg.setWndHeight(LOWORD(msg.lParam));
            if (msg.wParam == SIZE_MAXIMIZED)
                _cfg.setWndShow(SW_MAXIMIZE);
            else if (msg.wParam == SIZE_MINIMIZED)
                _cfg.setWndShow(SW_MINIMIZE);
            else if (msg.wParam == SIZE_RESTORED)
                _cfg.setWndShow(SW_SHOWNORMAL);
            break;
    }
}

The application object is interested in the window size because it stores this information in a configuration file.

When I run this in Visual Studio's debugger, the output window looks like this after resizing the window:

WM_SIZE received.
GUI message received.
GUI_MSG_SIZECHANGED received.
WM_SIZE received.
WM_SIZE received.
WM_SIZE received.
WM_SIZE received.
...etc...

The PostMessage() function never fails, but seems to only send GUI_MSG_SIZECHANGED (#defined as WM_APP + 0x000d) the first time WM_SIZE is handled, which is right after handling WM_CREATE.

I have no idea what could be causing this. I tried using SendMessage and PostThreadMessage but the result is the same. Also read through MSDN's message handling documentation but couldn't find what's wrong with my code.

Could anyone help?


Solution

  • Hacking a custom message loop is something you'll live to regret some day. You hit it early.

    Don't post messages with a NULL window handle, they can only work if you can guarantee that your program only ever pumps your custom message loop. You cannot make such a guarantee. These messages fall into the bit bucket as soon as you start a dialog or Windows decides to pump a message loop itself. Which is the case when the user resizes a window, the resize logic is modal. Windows pumps its own message loop, WM_ENTERSIZEMOVE announces it. This is also the reason that PostThreadMessage is evil if the thread is capable of displaying any window. Even a MessageBox is fatal. DispatchMessage cannot deliver the message.

    Create a hidden window that acts as the controller. Now you can detect GUI_MSG_SIZECHANGED in its window procedure and no hacks to the message loop are necessary. That controller is not infrequently the main window of your app btw.