2

У меня возникли проблемы с правильной работой цикла сообщений из обработчика сообщений. В действительности реплицирует, как DialogBox() обрабатывает сообщения, минус все окна.Рекурсивный цикл сообщений

Простое обращение GetMessage() из обработчика сообщений почти работает, за исключением случаев, когда событие WM_SYSKEYDOWN, открывающее системное меню, также запускает запись в подконтур. После этого происходят странные вещи: нажатие клавиш проглатывается, а сообщения WM_MOUSEMOVE относительно системного меню отправляются в главное окно.

Для записи это происходит как в Windows 8, так и в XP.

Чтобы дать некоторый контекст, я пытаюсь использовать поточную модель, где рабочий поток (без окон) обменивается сообщениями через блокировку SendMessage, обращается к главному окну, действующему в качестве сервера. Эти действия могут потребовать дополнительного ввода или зависят от других операций ввода-вывода, и поэтому регулярные сообщения должны обрабатываться до тех пор, пока ответ не будет готов.

Я абсолютно уверен, что это основная ошибка или недоразумение с моей стороны, как и в прошлый раз, когда я размещал здесь, но я не могу полностью понять, что я делаю неправильно самостоятельно.

Вот мой пример для воспроизведения. Попробуйте выполнить навигацию после нажатия ALT + SPACE, чтобы открыть системное меню,

#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; 
} 
+1

не делайте этого, вы будете сталкиваться только с проблемами один за другим. Если вам нужно сотрудничество потоков, вы можете использовать PostMessage/PostThreadMessage (с окном объекта или без него в рабочем потоке) – manuell

+0

Спасибо, я попробую обходное решение. Меня все еще беспокоит, что я не понимаю операции очереди сообщений после всех этих лет. Можете ли вы дать мне какие-либо подсказки относительно того, почему это не удается или какая часть контракта я нарушаю? – doynax

+1

Я не знаю, вероятно, связан с запуском нового цикла сообщений, пока предыдущий DispatchMessage() все еще работает. Лично я бы использовал ожидающее событие через 'CreateEvent()' вместо этого. Посмотрите на связанные функции, такие как 'SetEvent()', 'ResetEvent()', 'PulseEvent()' и 'WaitForSingleObject()'. –

ответ

1

Модальные петли сообщений отлично. У Раймонда Чена есть series of articles on writing modal message loops properly.

Одна вещь, которую я замечаю: Ваша нить должна сообщение сообщение, а не отправить его; SendMessage звонит прямо в окно proc. Не используйте также PostThreadMessage; который предназначен для потоков без видимого интерфейса (и nested DispatchMessage won't know how to dispatch the thread message, что приводит к отбрасыванию сообщений).

+0

Спасибо, я более подробно рассмотрю эту статью. Я намеренно использую SendMessage в этом случае, так как я хочу, чтобы вызов блокировался, пока окно не закончило обработку сообщения, что должно быть хорошо по потокам, если я правильно понял. Я использую PostMessage вместо этого, когда мне не нужно ждать (на самом деле SendNotifyMessage, поскольку они доставляются по порядку.) – doynax

+0

@doynax - если вам нужно заблокировать, используйте событие. [Перекрестная передача SendMessage часто приводит к взаимоблокировке] (http://www.developerfusion.com/article/1713/message-management/7/) и [другие нечетные сбои] (http://blogs.msdn.com/b /oldnewthing/archive/2004/11/19/266664.aspx). –

+0

Я надеялся, что сойду с ним без окон, но, возможно, есть еще крайние случаи, которые я не рассматривал. SendMessage довольно удобен как механизм блокировки, для получения результатов и возможности передачи локальных структур блоков параметров. – doynax