Conventional and Thread Message Loops

Conventional message loop

The first message loop we showed is a conventional message loop. It can be parameterized to look like the following:
.data
msgbuf MSG  <>

.code
call    setup         ; *** user defined setup ***
msg_loop:
push    large 0       ; uMsgFilterMax
push    large 0       ; uMsgFilterMin
push    large 0       ; hWnd (filter)
0 = all windows
push    offset msgbuf ; lpMsg
call    GetMessage    ; returns FALSE if WM_QUIT
or      eax
eax
jz      end_msg_loop

call    dispatch_message  ; *** user defined dispatch ***
jmp     msg_loop

end_msg_loop:
call    cleanup       ; *** user defined cleanup ***
If there are no messages waiting in the message queue GetMessage returns control to Windows. This frees up the CPU allowing it to be used by other programs. Alternatively we can say that GetMessage gives up the program's time slice the maximum amount of time given to the program before preemption. All this allows the whole system to be more responsive than otherwise.

The dispatch message code for a basic GUI app is:

dispatch_message:
push    offset msgbuf
call    TranslateMessage
push    offset msgbuf
call    DispatchMessage
ret
The call to TranslateMessage checks for keyboard messages and on some combinations of messages adds WM_CHAR messages to the message queue. (What a surprise -- WM_CHAR is not a keyboard message!) We show the dispatch code as a subroutine but this short sequence is usually inlined which eliminates the call-ret overhead.

Thread message loop

Each window runs its window proc in some thread. However a thread can run without creating any windows. You can still pass messages to the thread with PostThreadMessage. To handle the received messages use the conventional loop and just change the dispatch code to handle the messages directly.
dispatch_message:
mov     eax
msgbuf.msg_message
cmp     eax
WM_APP+0
je      on_app_message0
cmp     eax
WM_APP+1
je      on_app_message1
ret