How to handle WM_QUIT in nested message loop

Traditionally in Windows there is standard message loop that consist of GetMessage, TranslateMessage and DispatchMessage. There are variations but most cases it is like this. One popular case is to use PeekMessage instead of GetMessage. Usually this message loop is in center of your application and in many modern frameworks it is not even visible. It is mostly hidden from developer.

But in some cases, it is required to create your own message loop. There are many cases when it is beneficial to do. As result there is main message loop that processing messages and in a reaction on some message, second message loop is started. Then in reaction on some message, inner loop quits, and execution returns to main message loop. Normally nested message loop limits what user can do. For example, it can be loop that shows modal dialog and it will disable all other windows.

But in some cases, inner message loop allows user to continue to interact with all parts of application and this often creates a lot of problem. For example, imagine user opens some document and then when say user clicks on it, modal loop is started in one of the functions of that document. Then user closes document but there is still some code that running in already destroyed document. Or there can be something else that leads to the same problem.

Anyway, when you understand problem, you can easily find few solutions. But there is one tricky case when user closes application while you in nested modal loop. Imagine situation when there is nothing bad happens and object that running nested message loop is not destroyed. In many cases I seen message loop is written incorrectly. But before I explain why, I have to explain how application quits.

Usually when main window is destroyed, application calls PostQuitMessage(0). Instead of zero there can be anything. Usually it supposes to be exit code of application. And then after all messages in message queue processed and no messages left, GetMessage will return zero. PeekMessage will just return WM_QUIT message. Please note that GetMessage or PeekMessage will always process WM_QUIT no matter which values you specify to filter messages. So far so good. Where can be problem?

Very often nested message loop just quits loop when that code found that message is WM_QUIT. And it is incorrect. User attempts to close application but as result only inner message loop quits and that’s it. Main windows loop will not receive WM_QUIT. Moreover, code that handles closure of main window can leave application in state that not suitable to continue application execution. This code assumes that main loop will quit, and user simply cannot interact with application anymore. But because only nested loop quits, execution transfers to main message loop and user still can interact with application leading to crash or data corruptions.

But what is right way to deal with WM_QUIT? It is actually very simple. Once you receive WM_QUIT, you have to call PostQuitMessage and pass value of wParam to it. That’s it. Then when execution will return to main message loop, it will receive WM_QUIT as well and quit.

I hope it helps