「予想通り、WaitForSingleObject が待機し続け、アプリを適切に閉じることができない場合に、アプリケーションを閉じようとするまで、これはすべて正常に機能します。」
スレッドが何をしているかに関係なく、どのアプリも閉じることができます。アプリ内の任意のスレッドから ExitProcess(0) を呼び出すと、API/sychro で INFINITE を待機しているスレッド、スリープしているスレッド、別のプロセッサで実行中のスレッドなどがあっても、アプリは終了します。OS は、実行されていないすべてのスレッドの状態を「二度と実行しない」に変更し、プロセッサ間ドライバーを使用して、実際にスレッド コードを実行している他のプロセッサをハード割り込みします。すべてのスレッドが停止すると、OS はハンドルやセグメントなどを解放し、アプリは存在しなくなります。
アプリが閉じているときに、開発者がスタックしているスレッドを「きれいに」シャットダウンしようとすると、問題が発生します。そう..
OnClose/OnCloseQuery ハンドラ、FormDestroy、またはデストラクタに TThread.WaitFor などがありますか? スレッドを確実に終了させる重大な理由がない場合は、コメントアウトしてください。
これにより、メイン フォームを閉じることができるため、コードは最終的に、赤い十字ボタンをクリックしてから取得しようとしていた ExitProcess() に到達します。
もちろん、自分で ExitProcess() を呼び出すこともできますが、これにより、データベース接続などの他のプロセスでリソースがリークしたままになる可能性があります。
「スレッドを停止しないと、クローズ時に 216/217 エラーが発生します」。これは、開発者が「残念な」Delphi スレッドの例に従い、セカンダリ スレッド フィールドとメイン スレッド フィールドの間でデータを直接交換することによってスレッドと通信するためによく発生します (例: TThread.synchronize)。アプリの実行中であっても、フォームが破棄されてスレッドが書き込みを試みているとき、またはスレッドが破棄されてメインスレッドのフォームがその上で呼び出しメソッドを試しています。スレッドと非同期に通信する方が、両方とも存続する queueing/PostMessaging オブジェクトを使用する方がはるかに安全です。スレッド/フォームで作成され、フォーム/スレッドで、または (スレッドセーフな) 初期化セクションで作成されたオブジェクトのプールによって解放されるオブジェクト。
「フォーム ハンドルは閉じているため無効ですが、スレッドがそれにメッセージを投稿しようとしています」。PostMessage が例外の場合は、スレッドを終了します。より良い方法は、上記のアプローチに似ています。すべてのフォームよりも長生きするウィンドウにのみメッセージを投稿します。すべてのスレッドがポストに使用する 1 つの const メッセージ番号のみを処理する単純な WndProc を使用して、初期化セクションに作成します。wParam を使用して TwinControlインスタンスを渡すことができますスレッドが通信しようとしているもの (通常はフォーム変数) であり、lParam は通信中のオブジェクトを渡します。スレッドからメッセージを取得すると、WndProc は渡された TwinControl で「Peform」を呼び出し、TwinControl はメッセージ ハンドラで通信オブジェクトを取得します。たとえば、単純なグローバル ブール値「AppClosing」は、シャットダウン中に自分自身を解放する TwinControls で Peform() を呼び出す WndProc を停止できます。このアプローチは、OS が別のハンドルでフォーム ウィンドウを再作成するときに発生する問題も回避します。Delphi フォーム ハンドルは使用されず、Windows は初期化で作成された単純なフォームのハンドルを再作成/変更しません。
私は何十年もこれらのアプローチに従ってきましたが、数十のスレッドがキューにオブジェクトを投げかけているアプリでも、シャットダウンの問題は発生しません.
Rgds、マーティン