后台线程可以配置为接收窗口消息。您可以使用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
邮件并返回false的正确方法是什么。
我们已经了解到that the incorrect way to post a WM_QUIT
message要打电话:
PostThreadMessage(nThreadId, WM_QUIT, 0, 0);
甚至有examples out there where people employ this approach。来自MSDN:
请勿使用PostMessage功能发布 WM_QUIT 消息;使用PostQuitMessage。
正确的方式是“某人”致电PostQuitMessage
。 PostQuitMessage
is a special function,用于设置与消息队列关联的特殊标记,以便{I} GetMessage
在时间合适时合成WM_QUIT
消息。
如果这是一个与“ window ”关联的消息循环,那么标准设计模式是在窗口被销毁时,收到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
is sent by the Window Manager. It is sent when someone calls DestroyWindow
. It is wrong to just post 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;
但是有一种规范方法可以退出线程的消息循环吗?
事实证明,所有人最好不使用无窗口消息队列。如果您没有可以发送消息的窗口,很多事情可能是无意中的,并且巧妙地破坏了。
Instead allocate hidden window(例如使用Delphi的thread - unsafe AllocateHwnd
)并使用普通的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;
答案 0 :(得分:7)
没有规范的方式;没有佳能。您可以通过发布GetMessage
消息让wm_Quit
返回零,然后通过调用PostQuitMessage
来执行此操作。您何时以及如何知道这一点取决于您。
在响应wm_Destroy
时这很常见,但这只是因为退出程序的常用方法是关闭窗口。如果您没有关闭的窗口,请选择其他方式。您的wm_PleaseEndYourself
想法没问题。
您甚至不必发送消息。您可以使用某个可等待的对象(如事件或信号量),然后使用MsgWaitForMultipleObjects
检测是否在等待新消息时发出信号。
你甚至不必等待GetMessage
返回零。如果您已经知道线程需要停止,那么您可以完全停止处理消息。有很多方法可以退出循环。您可以使用exit
,break
,raise
甚至goto
,或者您可以设置一个标记,您可以在循环条件中检查{{{{ 1}}。
另请注意,GetMessage
在失败时返回-1,作为非零值将被解释为true。如果GetMessage
失败,您可能不希望继续发送消息循环,因此请检查GetMessage
,或者执行the documentation recommends。
答案 1 :(得分:5)
使用PostThreadMessage
不一定是不正确的。您链接的Raymond的文章说:
因为系统试图在"不好的时间注入WM_QUIT消息&#34 ;;相反,它等待着#34;安定下来"在生成WM_QUIT消息之前,从而减少程序可能处于由一系列发布消息触发的多步骤过程中的可能性。
如果此处列出的问题不适用于您的邮件队列,请使用PostThreadMessage
致电WM_QUIT
并自行解决。否则,您需要创建一个特殊信号,即用户定义的消息,允许您从线程中调用PostQuitMessage
。