バックグラウンドスレッドは、ウィンドウメッセージを受信するように構成できます。を使用してスレッドにメッセージを投稿します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を返す正しい方法は何ですか。
メッセージを投稿する間違った方法は、次の電話をかけることであることWM_QUIT
をすでに学びました。
PostThreadMessage(nThreadId, WM_QUIT, 0, 0);
人々がこのアプローチを採用している例さえあります。MSDNから:
PostMessage関数を使用してWM_QUITメッセージを投稿しないでください。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のスレッドを使用して-安全 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;