より多くの情報がなければ、これをデバッグするのを助けるのは難しいでしょう、特にそれが一方のサービスでは機能するがもう一方のサービスでは機能しない理由。でも:
コードの問題を修正しようとする代わりに、ウィンドウを完全に削除し、PostMessage()の代わりにPostThreadMessage()を使用することをお勧めします。メッセージの投稿が正しく機能するには、メッセージループが必要ですが、必ずしもウィンドウを受信する必要はありません。
編集:私はあなたのすべての答えに一度に返信しようとしています。
まず、生活を楽にしたい場合は、gabrのOmniThreadLibraryを実際にチェックする必要があります。それがWindowsサービスアプリケーションで機能するかどうかはわかりません。それがまだ試されているかどうかさえわかりません。あなたはフォーラムで尋ねることができます。ただし、多くの優れた機能があり、学習効果のためだけに検討する価値があります。
ただし、もちろん、これを自分でプログラムすることもできます。Delphi2007より前のDelphiバージョンをプログラムする必要があります。長年にわたって進化し、数十のプログラムで機能する内部ライブラリからスニペットを追加するだけです。しかし、バグがないとは言いません。あなたはそれをあなたのコードと比較することができます、そして何かが目立つならば、遠慮なく尋ねてください、そして私は明確にするように努めます。
これは、ワーカースレッドの基本クラスの簡略化されたExecute()メソッドです。
procedure TCustomTestThread.Execute;
var
Msg: TMsg;
begin
try
while not Terminated do begin
if (integer(GetMessage(Msg, HWND(0), 0, 0)) = -1) or Terminated then
break;
TranslateMessage(Msg);
DispatchMessage(Msg);
if Msg.Message = WM_USER then begin
// handle differently according to wParam and lParam
// ...
end;
end;
except
on E: Exception do begin
...
end;
end;
end;
例外が処理されないようにしないことが重要であるため、すべての周りにトップレベルの例外ハンドラーがあります。例外を使用して行うことは選択であり、アプリケーションによって異なりますが、すべての例外をキャッチする必要があります。そうしないと、アプリケーションが終了します。サービスでは、おそらくあなたの唯一のオプションはそれらをログに記録することです。
GetMessage()内にあるときにスレッドをウェイクアップする必要があるため、スレッドのシャットダウンを開始するための特別なメソッドがあります。
procedure TCustomTestThread.Shutdown;
begin
Terminate;
Cancel; // internal method dealing with worker objects used in thread
DoSendMessage(WM_QUIT);
end;
procedure TCustomTestThread.DoSendMessage(AMessage: Cardinal;
AWParam: integer = 0; ALParam: integer = 0);
begin
PostThreadMessage(ThreadID, AMessage, AWParam, ALParam);
end;
WM_QUITを投稿すると、メッセージループが終了します。ただし、子孫クラスのコードは、特にCOMインターフェイスが使用されている場合、スレッドのシャットダウン中にWindowsメッセージが適切に処理されることに依存する可能性があるという問題があります。そのため、単純なWaitFor()の代わりに、実行中のすべてのスレッドを解放するために次のコードが使用されます。
procedure TCustomTestController.BeforeDestruction;
var
i: integer;
ThreadHandle: THandle;
WaitRes: LongWord;
Msg: TMsg;
begin
inherited;
for i := Low(fPositionThreads) to High(fPositionThreads) do begin
if fPositionThreads[i] <> nil then try
ThreadHandle := fPositionThreads[i].Handle;
fPositionThreads[i].Shutdown;
while TRUE do begin
WaitRes := MsgWaitForMultipleObjects(1, ThreadHandle, FALSE, 30000,
QS_POSTMESSAGE or QS_SENDMESSAGE);
if WaitRes = WAIT_OBJECT_0 then begin
FreeAndNil(fPositionThreads[i]);
break;
end;
if WaitRes = WAIT_TIMEOUT then
break;
while PeekMessage(Msg, 0, 0, 0, PM_REMOVE) do begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end;
except
on E: Exception do
// ...
end;
fPositionThreads[i] := nil;
end;
end;
子孫コントローラークラスのデストラクタがスレッドが使用する可能性のあるオブジェクトの解放を開始する前に、すべてのスレッドを解放する必要があるため、これはオーバーライドされたBeforeDestruction()メソッドにあります。