Как выйти из цикла сообщений потока?

Можно настроить фоновый поток для получения оконных сообщений. Вы должны отправлять сообщения в поток, используя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;

13
задан Community 23 May 2017 в 12:02
поделиться

0 ответов

Другие вопросы по тегам:

Похожие вопросы: