I noted something else that surpised me. In the debugger I put a break in the message loop if the message was a WM_DESTROY or a WM_COMMAND and wParam IDCANCEL. THen I put another break in the WM_DESTROY message in the callback.

TO my surprise, when I clicked on the cancel button, the debugger stopped on the WM_DESTROY message in the callback, and not the PeekMessage() loop handler after the MsgWait...() function.

How did it do that? It must have gone through the main message loop instead of the wait function, but that's NOT how I understood things to work!

I am confused. I thought the PeekMessage() loop would see it first before the DispatchMessage() function.

Brian