I spent a lot of hours investigating this problem.
Initially the first part of the problem, because I was showing a dialog for "Really close?" and preventing closing if some files are not saved, this was done in FormCloseQuery and it was stupid approach, because FormCloseQuery doesn’t know if windows is shutting down and thus preventing shutdown.
Then, I made a Message listeners for windows asking applications query for shutdown : WM_QueryEndSession and WM_EndSession:
The problem remained: the messages were received only second time. The first time when Windows was shutting down the application was still preventing shutdown, and only for the second Shutdown call, the messages were received and Application quit.
So something else was corrupting even the messages queue.
After some time, I accidentally came under another Form in same application, which also had FormCloseQuery, this time it was really needed because this Form was closing with fading effect and Query was preventing close until fader finished. So that was the second part of the problem.
Again I needed to listen for Shutdown messages in this Form.
So to conclude: you need to listen for Windows shut down Messages and Queries in every form that has FormCloseQuery or better yet avoid it, if you want to let windows shutdown normally.
Also to note: Delphi made applications (on Windows shutdown) exit instantly without freeing objects, so be careful and it is a good practice to listen to Shutdown procedure and to call your own Closing and Freeing procedure manually:
bForceClose here is a global boolean variable, signaling that we should force our application closing, and if you still need FormCloseQuery check for bForceClose there (of course you need to set it false on application start):
And you need this done in every form with FormCloseQuery.