News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

What's the theory behind the "Message Pump"?

Started by CoR, January 31, 2007, 06:19:43 AM

Previous topic - Next topic

CoR

#15
Thanks zooba, it's all clear now  :U

@TomRiddle
Oh yes, I was lost  :wink And I am still far from knowledge I need to make dictionary. But I am getting there thanks to you guys  :bg

EDIT:
My goal is to learn asm. But in my way I need to have some project with meaning. Sort of a beacon, like you described, to group and gather my thoughts.

lingo

TomRiddle,

"Here, btw, is my message pump
...
...
JumpIfRegisterIsZero Eax, L_ExitLoop
"

I can't see how you manage the situation
with GetMessage return value = -1
Why?

from MSDN:
Return Value

If the function retrieves a message other than WM_QUIT, the return value is nonzero.

If the function retrieves the WM_QUIT message, the return value is zero.


!!!?

If there is an error, the return value is -1. For example, the function fails if hWnd is an invalid window handle or lpMsg is an invalid pointer. To get extended error information, call GetLastError.

Warning Because the return value can be nonzero, zero, or -1, avoid code like this:

while (GetMessage( lpMsg, hWnd, 0, 0)) ...
The possibility of a -1 return value means that such code can lead to fatal
application errors. Instead, use code like this:

BOOL bRet;

while( (bRet = GetMessage( &msg, hWnd, 0, 0 )) != 0)
{
    if (bRet == -1)
    {
        // handle the error and possibly exit
    }
    else
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}


Regards,
Lingo

Rockoon

I havent had problems not testing for -1 myself.

MSG:

How does your pointer to an MSG structure become invalid? I don't think it can in a standard message pump unless there are some very serious issues going on, issues that will likely break any code that follows anyways (such as the stack being fubar)

HWND:

He isnt passing an hwnd to GetMessage(). I am guessing its hard for Null to be an invalid window handle since its a special cased on the other side of the GetMessage() call to get grab all messages belonging to the calling thread (regardless of their hwnd)


When C++ compilers can be coerced to emit rcl and rcr, I *might* consider using one.

Seb

lingo, is it possible to get the full source code of your MMX message looper? Some parts appear to be missing.

Regards,
Seb

hutch--

Its been my experience that if there are errors in the code, the app fails before it gets to the message loop independently of whether the message loop tests for -1 as well as zero. This is my preferred message loop.


    keystroke equ <1>                   ; set to ZERO if no key processing

MsgLoop proc

    LOCAL msg:MSG

    push esi
    push edi
    xor edi, edi                        ; clear EDI
    lea esi, msg                        ; Structure address in ESI
    jmp jumpin

  StartLoop:
  IF keystroke
    invoke TranslateMessage, esi
  ENDIF
    invoke DispatchMessage,  esi
  jumpin:
    invoke GetMessage,esi,edi,edi,edi
    test eax, eax
    jnz StartLoop

    mov eax, msg.wParam
    pop edi
    pop esi

    ret

MsgLoop endp


Which disassembles to,


00401541                    fn_00401541:                ; Xref 004014D4
00401541 55                     push    ebp
00401542 8BEC                   mov     ebp,esp
00401544 83C4E4                 add     esp,0FFFFFFE4h
00401547 56                     push    esi
00401548 57                     push    edi
00401549 33FF                   xor     edi,edi
0040154B 8D75E4                 lea     esi,[ebp-1Ch]
0040154E EB0C                   jmp     loc_0040155C
00401550                    loc_00401550:               ; Xref 00401567
00401550 56                     push    esi
00401551 E8F4040000             call    jmp_TranslateMessage
00401556 56                     push    esi
00401557 E894040000             call    jmp_DispatchMessageA
0040155C                    loc_0040155C:               ; Xref 0040154E
0040155C 57                     push    edi
0040155D 57                     push    edi
0040155E 57                     push    edi
0040155F 56                     push    esi
00401560 E897040000             call    jmp_GetMessageA
00401565 85C0                   test    eax,eax
00401567 75E7                   jnz     loc_00401550
00401569 8B45EC                 mov     eax,[ebp-14h]
0040156C 5F                     pop     edi
0040156D 5E                     pop     esi
0040156E C9                     leave
0040156F C3                     ret
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

lingo

Seb,
"Some parts appear to be missing..."


OK, this works: :wink
.686
.mmx
;k3d
.xmm
.model  flat, stdcall
option  casemap:none,language:stdcall,dotname
assume  fs:nothing

include c:\masm32\include\windows.inc
include c:\masm32\include\gdi32.inc
include c:\masm32\include\user32.inc
include c:\masm32\include\kernel32.inc
include c:\masm32\include\Comctl32.inc
include c:\masm32\include\shell32.inc
include c:\masm32\include\advapi32.inc

includelib c:\masm32\lib\gdi32.lib
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
includelib c:\masm32\lib\Comctl32.lib
includelib c:\masm32\lib\shell32.lib
includelib c:\masm32\lib\ole32.lib
includelib c:\masm32\lib\advapi32.lib


.data

MemArray     dd 0,0,0,0,0,0,0,0,0,0,0,0  ;

hListView    dd 0
hMemOrdLists dd 0
hImageList   dd 0
hProcHeap    dd 0

CaptionText  db "Lingo's message loop",0
ClassName    db "lingoclass", 0
   
wc        dd sizeof WNDCLASSEX
          dd CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW
          dd offset WndProc, 0, 0, 400000h, 0, 0
          dd COLOR_WINDOW +1, 0
          dd offset  ClassName, 0
align 8       
MyStack   dd offset  TranslateMessage ; ret address for GetMessage
          dd offset  msg              ; parameter for GetMessage
hWnd      dd 0                        ; hWnd ; parameter for GetMessage
          dd 0                        ; parameter for GetMessage
          dd 0                        ; parameter for GetMessage
          dd offset  DispatchMessage  ; ret address for  TranslateMessage
          dd offset  msg              ; parameter for  TranslateMessage
          dd offset  Mess_Loop        ; ret address for  DispatchMessage
          dd offset  msg              ; parameter for  DispatchMessage
          dd offset GetMessage        ;
          dd 0                        ; one dd more for 8 bytes allignment
msg       MSG  <>

.code
Start:
              push IDI_APPLICATION
              push 0
              call LoadIcon
              mov wc.WNDCLASSEX.hIcon, eax
              mov wc.WNDCLASSEX.hIconSm, eax
              push IDC_ARROW
              push 0
              call LoadCursor
              mov wc.WNDCLASSEX.hCursor, eax
              push offset wc   
              call RegisterClassEx      ; Register Main Window Class

              push 0 ; Create Main Window
              push 400000h
              push 0
              push 0
              push 680
              push 800
              push 2
              push 180
              push WS_OVERLAPPEDWINDOW
              push offset CaptionText
              push offset ClassName
              push 0
              call CreateWindowEx
              mov hWnd, eax

              push eax
              push SW_SHOWNORMAL
              push eax
              call ShowWindow
              call UpdateWindow
;
                mov  edi, dword ptr [GetMessage+2] ; edi->jmp address of GetMessage API
                mov  esi, offset MyStack
                mov  edi, [edi]      ; edi->real address of GetMessage API
                add  esp, -6*4       
                movq MM3, [esi+8]    ; esi-> offset MyStack
                movq MM5, [esi+28]
                pxor MM4,     MM4
                movq [esp],   MM5
                movq [esp+8], MM3
                movq [esp+16],MM4
                jmp edi              ; edi->real address of GetMessage API
;Message Loop
Align 8                             
Mess_Loop:                   
                add     esp, -9*4            ;       
                movq    MM3, [esi+0*8]       ; esi-> offset MyStack
                movq    MM4, [esi+1*8]       ;
                movq    MM5, [esi+2*8]       ;
                movq    MM6, [esi+3*8]       ;
                movq    MM7, [esi+4*8]       ;
                movq    [esp+0*8],  MM3      ;
                movq    [esp+1*8],  MM4      ;
                movq    [esp+2*8],  MM5      ;
                movq    [esp+3*8],  MM6      ;
                movq    [esp+4*8],  MM7      ;
                jmp     edi                  ; edi->real address of GetMessage API
                                     
;Exit Message Loop
MLoopExit:                 
                                     
;FreeMemArray                         ;
     ;   mov      ebx, MemArray       ;
         xor      edi, edi            ;
     ;   mov      ecx, [ebx]          ;
@@:                                   ;
     ;   test     ecx, ecx            ; 
     ;   je       @f                  ;
     ;   mov      [ebx], edi          ; edi = 0
     ;   add      ebx, 4              ;
     ;   invoke   VirtualFree, ecx, edi, MEM_RELEASE
     ;   mov      ecx, [ebx]
     ;   jmp      @b
@@:     
     ;   invoke   VirtualFree, hMemOrdLists, 0, MEM_RELEASE
     ;   invoke   ImageList_Destroy, hImageList
     ;   invoke   HeapFree, hProcHeap, edi, MemArray
        push     edi                 ; edi = 0
        jmp      ExitProcess         ; Exit

ALign 16    ; [esp+0*4]->ret address, [esp+1*4]->hWnd, [esp+2*4]->uMsg
WndProc:    ; [esp+3*4]->wParam, [esp+4*4]->lParam

        cmp dword ptr [esp+2*4], WM_NOTIFY
        je  Lwm_notify
        cmp dword ptr [esp+2*4], WM_SIZE
        je  Lwm_size
        cmp dword ptr [esp+2*4], WM_DESTROY ; uMsg
        jne DefWindowProc
        je  MLoopExit                       ; Exit

Lwm_size:
        mov ecx, [esp+4*4]                  ; lParam
        mov eax, ecx                        ;
        and ecx, 0FFFFh                     ; ecx = height
        shr eax, 16
       ;mov edx, hListView
       ;invoke MoveWindow, edx, 0, 0, ecx, eax, 1
        jmp DefWindowProc

Lwm_notify:
        mov edx, [esp+4*4]                  ; lParam
       ;mov ecx, hListView   
       ;cmp ecx, [edx].LV_DISPINFO.hdr.hwndFrom
        jne DefWindowProc
       ;....
       ;....         
        je DefWindowProc
End Start

Regards,
Lingo

[attachment deleted by admin]

Seb


hutch--

Here is my contribution to useless programming for the day, an SSE message loop.


; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««

MsgLoop proc

    .data
      align 16
      gma dq 0
      mar dq 0
      msg MSG <0>
    .code

    push esi
    push edi
    sub edi, edi                        ; clear EDI
    mov esi, OFFSET msg                 ; Structure address in ESI

    mov eax, OFFSET gma
    mov [eax], esi                      ; load stack arguments into array
    mov [eax+4], edi
    mov [eax+8], edi
    mov [eax+12], edi
    movdqa xmm0, gma                    ; load aligned array into XMM register

    mov eax, OFFSET mar
    mov [eax], esi
    mov [eax+4], esi
    movq mm0, mar                       ; load array into MMX register
    sub esp, 16
    jmp jumpin

  StartLoop:
    sub esp, 24
    movntq [esp], mm0
    call TranslateMessage
    call DispatchMessage
  jumpin:
    movntdq [esp], xmm0                 ; write the 4 stack parameters
    call GetMessage
    test eax, eax                       ; test for zero
    jnz StartLoop

    mov eax, msg.wParam
    pop edi
    pop esi

    ret

MsgLoop endp

; «««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««««


You can run this one if the box is late enough wih the smug satisfaction that it may be a pico-second or two faster than a normal one.  :bg It builds with any version of ML.EXE from 6.15 upwards.

[attachment deleted by admin]
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

daydreamer

you are really funny, why dont you use peekmessage instead of getmessage?
optimize the overhead on a proc like getmessage is invain, when all getmessage do is looping until it receives a message

hutch--

magnus,

You are right but I warned that it was useless. With 3 API calls its a "who cares" with a message loop.  :bg
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

u

Quote from: daydreamer on February 12, 2007, 05:38:35 AMall getmessage do is looping until it receives a message
Incorrect - GetMessage goes to ring0 immediately, and the thread uses no more than a few cycles on each timeslice (once every 10ms) it's been given - after it sees there're no messages available, it immediately forfeits its timeslice to another thread [actually the kernel suspends it until the thread receives a message, so the overhead of one extra thread-switching is eliminated in the task-scheduler]. You couldn't possibly optimize it further.

Using mmx without "emms" will slow-down or crash the app when an OS-managed GUI object uses the FPU.  :toothy
Please use a smaller graphic in your signature.

u

Just to back-up my words, wrote a simple app

main proc
local msg:MSG
invoke GetMessage,addr msg,0,0,0
ret
main endp
start:
invoke main
invoke ExitProcess,0
end start


, and had it run for 15+ minutes. Every several minutes it'd do 4 context-switches in a flash (probably to check timeouts). Otherwise, the thread is constantly in Wait:WrUserRequest state (almost identical to suspended) .



In comparison, an infinite loop in user-mode of a normal-priority thread takes 400 thread-switches per second, and 95%+ cpu. So, a PeekMessage custom implementation is not recommended for normal GUI apps :wink
Please use a smaller graphic in your signature.

hutch--

I would agree with this comment (So, a PeekMessage custom implementation is not recommended for normal GUI apps) as GetMessage simply does not return if there is no message to be processed by the thread that it is called from. I think what Magnus had in mind was a games type message loop that hogged almost all of the processor to ensure that that games was not laggy or suffered interference from other processes while it was running.

There are probably better ways of doing this and among them is setting the process and/or thread priority but it would depend on theinternal architecture of the game as to how it sequenced events.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

daydreamer

Quote from: hutch-- on February 12, 2007, 12:46:52 PM
I would agree with this comment (So, a PeekMessage custom implementation is not recommended for normal GUI apps) as GetMessage simply does not return if there is no message to be processed by the thread that it is called from. I think what Magnus had in mind was a games type message loop that hogged almost all of the processor to ensure that that games was not laggy or suffered interference from other processes while it was running.

There are probably better ways of doing this and among them is setting the process and/or thread priority but it would depend on theinternal architecture of the game as to how it sequenced events.
I shouldnt brought up that C++ cheat here when assembly is superior when it comes to clean alternatives, we should keep most api calls in the main section and put heavy processes in workerthreads, because a workerthread entirely without using calls we have all general 8 regs free to use

ic2

I don't under stand how the Lingo MessLoop is making it possible to jxx DefWindowProc four times and there is no label for  DefWindowProc: to jump to ... but the code works and i can plainly see with the necked eye a faster Window popping up .  Can someone tell me what's going on here.  Where are these jumps going to and what is it doing under the hood ?  Is there a DefWindowProc under there somewhere ?