'All this works fine until I try to close the application when, as expected, WaitForSingleObject continues to wait and does not allow the application to close properly.'
Any application can close, no matter what its threads do. If you call ExitProcess (0) from any thread in your application, the application will close, regardless of whether there are threads waiting for INFINITE on some APIs / sychro, sleeping, running on another processor, whatever. The OS will change the state of all thereds that don't start so that they never run again, and use its interprocessor system driver to hard interrupt any other processors that actually run your thread code. Once all threads are stopped, the OS frees descriptors, segments, etc., and your application no longer exists.
Problems arise when developers try to "cleanly" close threads that are stuck - like yours when the application closes. So..
Do you have TThread.WaitFor or similar in the OnClose / OnCloseQuery, FormDestroy, or destructor handler? If you have, and you have no significant reason for the thread to be interrupted, just comment on it!
This allows you to close the main form and thus your code will finally reach the ExitProcess () that it was trying to get to, since you clicked on the red cross button
You can, compartment, just call ExitProcess () yourself, but this can lead to a drain of resources, for example, in other processes - database connections.
'216/217 closing errors if I do not stop threads. This is often due to developers following the examples of βfailedβ Delphi threads and interacting with threads, directly exchanging data between the fields of the secondary thread and the fields of the main thread (for example, TThread.synchronize). It just sucks and struggles to cause problems even when the application starts, not paying attention to stopping when the form has been destroyed, and the stream is trying to write to it or the stream has been destroyed, and the main stream form using call methods on it. It is much safer to communicate asynchronously with threads through the queue / PostMessaging objects that survive both of them, for example. objects created in the stream / form and freed in the form / stream, or using the (thread safe) pool of objects created in the initialization section. Forms can then be closed / freed safely, while related threads can continue to uselessly populate objects for processing until the main form is closed, ExitProcess () is reached, and the OS cancels the threads.
"My form handle is invalid because it is closed, but my thread is trying to send a message to it." If PostMessage excludes, exit the stream. The best way is similar to the above approach - send messages only to the window, which goes through all forms. Create it in the initialization section with the trivial WndProc, which processes only one const message number, which all threads use for publication. You can use wParam to transfer the TwinControl instance that the thread is trying to contact (usually a form variable), while lParam passes the object being passed. When it receives a message from a stream, WndProc calls "Peform" on the passed TwinControl, and TwinControl receives the comms object in the message handler. A simple global boolean "AppClosing", say, can stop WndProc calling Peform () on TwinControls, which are freed up during shutdown. This approach also avoids the problems that occur when the OS recreates the form window using a different descriptor - the Delphi form descriptor is not used, and Windows will not recreate / modify the simple form descriptor created during initialization.
I have been following these approaches for decades and have not experienced any problems with stopping, even with applications with dozens of threads building objects around in queues.
Rgds, Martin