OK, here's the solution. It has to do with an undocumented behavior of the mouse hooks. Although MS doesn't tell us so, a mouse hook is triggered when PeekMessage or GetMessage finds a mouse message on the queue. MS would have us believe that this hook has nothing to do with picking up the message, but it's not true. Normally these mouse messages only hit the queue for a window one at a time, however, if the mouse is CLICKED then TWO end up on the queue for the window. In the code above, the first message, WM_LBUTTONDOWN, gets processed by the hook, which then calls PeekMessage in it's loop. PeekMessage finds WM_LBUTTONUP in the queue, not that we care because we are really just telling windows not to write us off. However, Windows DOES care, since this next message was a mouse message, and queues up another call for the mouse hook to complete as soon as the current one is done. After exiting the current hook function call, Windows calls it again, this time for WM_LBUTTONUP, however, WM_LBUTTONUP was never removed from the message queue, so it is STILL the message that PeekMessage will find, queueing up another call for the current function. This happens infinitely, causing an infinite loop that prevents any other code in the application from running, but without taking up much processor time, or locking the user out, since moving the mouse upsets the delicate balance required to make this work. It creates a great bug though when the user clicks...and waits...and waits...and waits, and when they finally get impatient and move the mouse, the screen comes up. The fix is very simple. We don't care what PeekMessage finds, if anything, so change the two NULL parameters to WM_QUIT. This will make PeekMessage ONLY serve up any WM_QUIT messages that it finds (which it will do anyway no matter what.) Since no mouse messages are served up, the bug goes away.