Можно настроить фоновый поток для получения оконных сообщений. Вы должны отправлять сообщения в поток, используяPostThreadMessage
. Как правильно выйти из этого цикла сообщений?
Прежде чем отправлять сообщения в фоновый поток, поток должен убедиться, что очередь сообщений создана путем вызова PeekMessage
:
procedure ThreadProcedure;
var
msg: TMsg;
begin
//Call PeekMessage to force the system to create the message queue.
PeekMessage(msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
end;
Теперь внешний мир может отправлять сообщения в наш поток:
PostThreadMessage(nThreadID, WM_ReadyATractorBeam, 0, 0);
и наш поток находится в цикле GetMessage
:
procedure ThreadProcedure;
var
msg: TMsg;
begin
//Call PeekMessage to force the system to create the message queue.
PeekMessage(msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
//Start our message pumping loop.
//GetMessage will return false when it receives a WM_QUIT
// GetMessage can return -1 if there's an error
// Delphi LongBool interprets non-zero as true.
// If GetMessage *does* fail, then msg will not be valid.
// We want some way to handle that.
//Invalid:
//while (GetMessage(msg, 0, 0, 0)) do
//Better:
while LongInt(GetMessage(msg, 0, 0, 0)) > 0 do
begin
case msg.message of
WM_ReadyATractorBeam: ReadyTractorBeam;
// No point in calling Translate/Dispatch if there's no window associated.
// Dispatch will just throw the message away
// else
// TranslateMessage(Msg);
// DispatchMessage(Msg);
// end;
end;
end;
Мой вопрос заключается в том, как правильно GetMessage
получить WM_QUIT
сообщение и вернуть ложь.
Мы уже узнали , что неправильныйспособ отправки WM_QUIT
сообщениязаключается в вызове:
PostThreadMessage(nThreadId, WM_QUIT, 0, 0);
Существует даже примеров там, где люди применяют этот подход. Из MSDN:
Не публикуйте сообщение WM_QUITс помощью функцииPostMessage ; используйтеPostQuitMessage .
Правильный способсостоит в том, чтобы «кто-то» вызывалPostQuitMessage
. PostQuitMessage
— это специальная функция, которая устанавливает специальный флаг, связанный с очередью сообщений, чтобы GetMessage
синтезировала сообщение WM_QUIT
в нужное время. .
Если бы это был цикл обработки сообщений, связанный с " окном", то стандартный шаблон проектирования - когда окно уничтожается и приходит сообщение WM_DESTROY
, мы перехватываем и вызовите PostQuitMessage
:
procedure ThreadProcedure;
var
msg: TMsg;
begin
//Call PeekMessage to force the system to create the message queue.
PeekMessage(msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
//Start our message pumping loop.
//GetMessage will return false when it receives a WM_QUIT
while Longint(GetMessage(msg, 0, 0, 0)) > 0 do
begin
case msg.message of
WM_ReadyATractorBeam: ReadyTractorBeam;
WM_DESTROY: PostQuitMessage(0);
end;
end;
end;
Проблема в том, что WM_DESTROY
отправляется оконным менеджером. Он отправляется, когда кто-то вызывает DestroyWindow
. Неправильно просто публиковать WM_DESTROY
.
Теперь я мог бысинтезировать искусственное WM_PleaseEndYourself
сообщение:
PostThreadMessage(nThreadID, WM_PleaseEndYourself, 0, 0);
и затем обработать его в цикле сообщений моего потока:
procedure ThreadProcedure;
var
msg: TMsg;
begin
//Call PeekMessage to force the system to create the message queue.
PeekMessage(msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
//Start our message pumping loop.
//GetMessage will return false when it receives a WM_QUIT
while Longint(GetMessage(msg, 0, 0, 0)) > 0 do
begin
case msg.message of
WM_ReadyATractorBeam: ReadyTractorBeam;
WM_PleaseEndYourself: PostQuitMessage(0);
end;
end;
end;
Но существует ли канонический] способ выйти из цикла сообщений потока?
Оказывается, для всехбудет лучше, если вы не будетеиспользовать очередь сообщений без окон. Многие вещи могут быть непреднамеренно и незаметно сломаны, если у вас нет окна для отправки сообщений.
Вместо этого выделяйте скрытое окно(например, с помощью Delphi thread- unsafeAllocateHwnd
) и отправляйте в него сообщения, используя старый добрыйPostMessage
:
procedure TMyThread.Execute;
var
msg: TMsg;
begin
Fhwnd := AllocateHwnd(WindowProc);
if Fhwnd = 0 then Exit;
try
while Longint(GetMessage(msg, 0, 0, 0)) > 0 do
begin
TranslateMessage(msg);
DispatchMessage(msg);
end;
finally
DeallocateHwnd(Fhwnd);
Fhwnd := 0;
end;
end;
Где у нас может быть простая старая оконная процедура для обработки сообщений:
WM_TerminateYourself = WM_APP + 1;
procedure TMyThread.WindowProc(var msg: TMessage);
begin
case msg.Msg of
WM_ReadyATractorBeam: ReadyTractorBeam;
WM_TerminateYourself: PostQuitMessage(0);
else
msg.Result := DefWindowProc(Fhwnd, msg.msg, msg.wParam, msg.lParam);
end;
end;
и когда вы хотите, чтобы поток завершился, вы говорите ей:
procedure TMyThread.Terminate;
begin
PostMessage(Fhwnd, WM_TerminateYourself, 0, 0);
end;