#c #windows #multithreading #winapi #message-loop
#c #Windows #многопоточность #winapi #цикл сообщений
Вопрос:
Я пытаюсь создать программу WIN32 (C ), в которой мне придется одновременно обрабатывать сообщения и запускать цикл while. Чтобы сделать это, я хочу использовать потоки.
Когда я переместил цикл сообщений в отдельную процедуру (вызываемую из функции WinMain), все работало нормально. Однако, когда я использовал приведенный ниже код для потоковой передачи этой процедуры, вместо простого вызова ее из основного процесса, окно перестает отвечать.
Вы знаете, почему это происходит?
Внутри WinMain, после создания главного окна, я удалил цикл сообщений и возвращаемое значение, добавив следующий фрагмент кода:
std::thread t1(message_loop);
t1.join();
return return_val;
return_val
это глобальная переменная, которую я буду использовать для получения значения, которое WinMain должен вернуть по завершении цикла сообщений.
Кроме того, функция message_loop выглядит следующим образом:
void message_loop()
{
MSG messages;
while (GetMessage (amp;messages, NULL, 0, 0))
{
/* Translate virtual-key messages into character messages */
TranslateMessage(amp;messages);
/* Send message to WindowProcedure */
DispatchMessage(amp;messages);
}
return_val = messages.wParam;
}
Комментарии:
1. Вообще говоря, разделение кода создания пользовательского интерфейса и кода события пользовательского интерфейса на отдельные потоки не будет работать должным образом. Просто используйте один поток для всего пользовательского интерфейса (создание, модификация и обработка событий). Если вам нужны рабочие потоки для выполнения некоторой обработки, они могут делать это, пока они обрабатываются только (т. Е. Никак не используют пользовательский интерфейс).
2. Посмотрите на docs.microsoft.com/en-us/windows/win32/winmsg/… По сути, Windows создает очереди событий только в потоках, которые создали Windows, и эти очереди обрабатывают только события для Windows, созданные этим потоком. В вашем случае основной поток создает окно, а вы создаете цикл событий в другом потоке, поэтому он не будет видеть события для окон, созданные основным потоком. Вот почему обычно программы Windows создают окна в главном потоке, настраивают цикл событий в главном потоке, а другие потоки напрямую не взаимодействуют с графическим интерфейсом.
3. @Peter: Этого не хватает
PostThreadMessage
. Не все сообщения связаны сHWND
. Поэтому Windows также необходимо создать очередь сообщений потока в потоках без Windows. Актуально, потому чтоPostThreadMessage
это обычная функция для отправки сообщений в такие фоновые потоки.4. Обратите внимание, что начальный поток в процессе — это базовый поток NT, у которого нет очереди сообщений, поэтому отправка сообщения в него с помощью
PostThreadMessageW
завершается ошибкой с кодом ошибкиERROR_INVALID_THREAD_ID
. Когда процесс загружается user32.dll он подключается к оконной станции и рабочему столу (обычно «WinSta0 Default»), а его структуры процессов ядра и потоков расширяются для взаимодействия со средой рабочего стола, что включает добавление очереди сообщений к каждому потоку в процессе.5. @MSalters — Мой предыдущий комментарий касался вопроса о том, почему рабочий поток не может обрабатывать события, отправленные в окно, созданное основным потоком. Но, да, существуют другие методы публикации событий в очередь событий потока.
Ответ №1:
Основная причина заключается в том, что Windows имеет понятие очереди сообщений потока. У каждого потока есть своя очередь сообщений. Можно запускать GetMessage
в потоке, но при этом будут получены только сообщения, принадлежащие этому потоку, например, для Windows, которые вы создаете в этом потоке. Сообщения, принадлежащие любому другому потоку (независимо от того, как был создан этот другой поток), не будут видны в вашем потоке.
Как вы заявляете, ваш std::thread
создается только «после создания главного окна». Это означает, что у вас есть две очереди сообщений потока; одна из основного потока, который создал главное окно, и другая очередь для вашего std::thread
. Последняя очередь останется пустой.
Вы видите это во втором аргументе для GetMessage
— передача HWND=0
там означает «все сообщения для этого потока».
Теоретически у вас может быть несколько потоков для нескольких окон, но это быстро становится очень сложным. Итак, на практике наиболее распространенным решением является использование main
потока, и единственной разумной альтернативой является наличие одного выделенного потока.
Комментарии:
1. » Это нормально для запуска
GetMessage
наstd::thread
тогда и только тогда, когда вы также вызывалиCreateWindow
то же самоеstd::thread
» — неверно. Окно не требуется.GetMessage()
также работает с сообщениями потока черезPostThreadMessage()
. И вызов любой функции USER32 в потоке, включаяGetMessage()
, автоматически создаст очередь сообщений для этого потока. Реальная проблема заключается в том, что окно имеет привязку к потоку , только поток, который создает окно, может извлекать сообщения для этого окна. Вероятно, это то, что вы пытались описать.2. @RemyLebeau: Привязка к потоку немного сильнее, чем просто обработка сообщений, ИМХО. Сходство с потоком также означает, что многие функции, работающие с HWND, могут быть вызваны только из потока-создателя. Например,
GetDC
обычно вызывается в ответ наWM_PAINT
, но он также должен быть вызван из того же потока, который получилWM_PAINT
. Что касаетсяPostThreadMessage
, я даже заметил то же самое в комментарии — обновил эту часть.