doynax
doynax

Reputation: 4455

Recursive message loop

I am having trouble correctly running a message loop from within a message handler. In effect replicating how DialogBox() processes messages, minus all of the windowing.

Simply invoking GetMessage() from within a message handler nearly works except when the WM_SYSKEYDOWN event opening the system menu also triggers the entry into a sub-loop. After this weird things happen, with keys presses being swallowed and WM_MOUSEMOVE messages relative to the system menu getting sent to the main window.

For the record this happens both in Windows 8 and XP.

To give some context I am attempting a threading model where a (windowless) worker thread communicates through blocking SendMessage calls back to the main window acting as a server. These actions may require further input or depend on other I/O and thus regular messages need to be processed until the reply is ready.

I'm fairly certain that this is a basic mistake or misunderstanding on my part, just like last time I posted here, but I can't quite seem to work out what I'm doing wrong on my own.

Here is my repro case. Try navigating after pressing ALT+SPACE to open the system menu,

#include <windows.h>

BOOL update;

LRESULT WINAPI WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    MSG msg;
    char text[256];
    switch(uMsg) {
    case WM_DESTROY:
        ExitProcess(0);
    // Trigger an update on input
    case WM_SYSKEYDOWN:
        update = TRUE;
        break;
    // Display the update from the worker thread, returning once it is time to
    // ask for the next one
    case WM_USER:
        wsprintf(text, TEXT("%u"), (unsigned int) lParam);
        SetWindowText(hwnd, text);
        while(!update && GetMessage(&msg, NULL, 0, 0) > 0) {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        update = FALSE;
        return 0;
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

DWORD WINAPI ThreadProc(void *hwnd) {
    // Submit updates as quickly as possible
    LONG sequence = 1;
    for(;;)
        SendMessage(hwnd, WM_USER, 0, sequence++);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCommandLine, int nCmdShow) {
    HWND hwnd;
    MSG msg;

    // Create our window
    WNDCLASS windowClass = { 0 };
    windowClass.lpfnWndProc = WindowProc;
    windowClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    windowClass.hCursor = NULL;
    windowClass.lpszClassName = TEXT("Repro");
    RegisterClass(&windowClass);
    hwnd = CreateWindow(TEXT("Repro"), TEXT("Repro"),
        WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,
        hInstance, 0);
    // Launch the worker thread
    CreateThread(NULL, 0, ThreadProc, hwnd, 0, NULL);
    // And run the primary message loop
    while(GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}

Upvotes: 2

Views: 1133

Answers (1)

Eric Brown
Eric Brown

Reputation: 13942

Modal message loops are perfectly fine. Raymond Chen has a series of articles on writing modal message loops properly.

One thing I notice: Your thread should post the message, not send it; SendMessage calls directly into the window proc. Don't use PostThreadMessage, either; that's designed for threads without visible UI (and the nested DispatchMessage won't know how to dispatch the thread message, resulting in dropped messages).

Upvotes: 1

Related Questions