- The message loop calls
GetMessage()
, which looks in your message queue. If the message queue is empty your program basically stops and waits for one (it Blocks). - When an event occures causing a message to be added to the queue (for example the system registers a mouse click)
GetMessages()
returns a positive value indicating there is a message to be processed, and that it has filled in the members of theMSG
structure we passed it. It returns0
if it hitsWM_QUIT
, and a negative value if an error occured. - We take the message (in the
Msg
variable) and pass it toTranslateMessage()
, this does a bit of additional processing, translating virtual key messages into character messages. This step is actually optional, but certain things won't work if it's not there. - Once that's done we pass the message to
DispatchMessage()
. WhatDispatchMessage()
does is take the message, checks which window it is for and then looks up the Window Procedure for the window. It then calls that procedure, sending as parameters the handle of the window, the message, andwParam
andlParam
. - In your window procedure you check the message and it's parameters, and do whatever you want with them! If you aren't handling the specific message, you almost always call
DefWindowProc()
which will perform the default actions for you (which often means it does nothing). - Once you have finished processing the message, your windows procedure returns,
DispatchMessage()
returns, and we go back to the beginning of the loop.
This is a very important concept for windows programs. Your window procedure is not magically called by the system, in effect you call it yourself indirectly by calling DispatchMessage(). If you wanted, you could use GetWindowLong() on the window handle that the message is destined for to look up the window's procedure and call it directly!
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
I tried this with the previous example code, and it does work, however there are various issues such as Unicode/ANSI translation, calling timer callbacks and so forth that this method will not account for, and very likely will break all but trivial applications. So do it to try it, but don't do it in real code :)
Notice that we use GetWindowLong() to retreive the window procedure associated with the window. Why don't we just call our WndProc() directly? Well our message loop is responsible for ALL of the windows in our program, this includes things like buttons and list boxes that have their own window procedures, so we need to make sure that we call the right procedure for the window. Since more than one window can use the same window procedure, the first parameter (the handle to the window) is used to tell the window procedure which window the message is intended for.
As you can see, your application spends the majority of it's time spinning round and round in this message loop, where you joyfully send out messages to the happy windows that will process them. But what do you do when you want your program to exit? Since we're using a while() loop, if GetMessage() were to return FALSE (aka 0), the loop would end and we would reach the end of our WinMain() thus exiting the program. This is exactly what PostQuitMessage() accomplishes. It places a WM_QUIT message into the queue, and instead of returning a positive value, GetMessage() fills in the Msg structure and returns 0. At this point, the wParam member of Msg contains the value that you passed to PostQuitMessage() and you can either ignore it, or return it from WinMain() which will then be used as the exit code when the process terminates.