News:

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

The window callback function

Started by zemtex, September 04, 2010, 06:48:37 PM

Previous topic - Next topic

zemtex

Hello.

When you use getmessage, it waits until a message is available, and then you naturally dispatch the message to the windows message handler function which you passed in the structure when you registered the window.

My question is why do we need such a function when you can handle the messages in a messageloop directly, using peekmessage? I don't get it why this function is so important. I find myself handling some important messages just beneath the peekmessage call and the rest in the callback function, it seems rather messy. Can anyone explain the purpose of that callback function?

EDIT: I think I got the answer with a little bit of thinking. Perhaps its important to get messages right there when they are available? Important messages may need instant handling? (But then again, thats nothing you cant do in a loop or just beneath getmessage or peekmessage)
I have been puzzling with lego bricks all my life. I know how to do this. When Peter, at age 6 is competing with me, I find it extremely neccessary to show him that I can puzzle bricks better than him, because he is so damn talented that all that is called rational has gone haywire.

xandaz

    Would it work if you didnt mention lpfnWndProc in WNDCLASSEX? Well, i think you'll need to accept the fact that windows is designed this way. I'm with you all the way i think. It's not a very programmer friendly platform, but hey, there's even more confusing things than that. So bare with it.
   Courage zemtex.
   Bye

baltoro

Raymond Chen has a number of blog entries detailing the common mistakes that programmers make in their message loops. It's a two page monthly listing,... you have to scroll through the stuff that doesn't apply.
The MSDN page that explains Using Messages and Message Queues also has lots of information.
Quote from: MSDNYou can use the PeekMessage function to examine a message queue during a lengthy operation. PeekMessage is similar to the GetMessage function; both check a message queue for a message that matches the filter criteria and then copy the message to an MSG structure. The main difference between the two functions is that GetMessage does not return until a message matching the filter criteria is placed in the queue, whereas PeekMessage returns immediately regardless of whether a message is in the queue.
Baltoro

redskull

Message queues are associated with a particular thread, and a particular thread may create any number of Windows.  With your method, it would require parsing the returned message to figure out which window it was destined for, and then having logic within each message type for each window; a programming nightmare.  For example

PeekMessage()
if message = Click
    if Window = a
         Do_Something_on_Window_A
    if Window = b
         Do_Something_on_Window_B
if message = Key
   if Window = a
         Do_Something_Else_on_Window_A
    if Window = b
         Do_Something_Else_on_Window_B     



Now scale that to 50, and you can see the problem.  With the current way, each window has it's own, encapsulated procedure, and only has to worry about messages destined for it.

-r
Strange women, lying in ponds, distributing swords, is no basis for a system of government

lingo

zemtex,
Congratulations about "non-standard" thinking but it is very old "invention" (see the link) :green

redskull is right about different windows but don't worry
coz you will not use two slow API's - TranslateMessage and DispatchMessage
and you have a lot of time in the main loop to create the logic about each message type for each window...
It is not mandatory to have main AND callback functions too. 
So, you can work with EBX,EBP,ESI and EDI registers without preservation too,
coz you have main proc only (and it is not a callback proc).

I just did a copy of the old example from link :U

.386                          ; minimum processor needed for 32 bit
      .model flat, stdcall    ; FLAT memory model & STDCALL calling
      option casemap :none    ; set code to case sensitive

      include \masm32\include\windows.inc
      include \masm32\include\user32.inc
      include \masm32\include\gdi32.inc
      include \masm32\include\kernel32.inc
      includelib \masm32\lib\user32.lib
      includelib \masm32\lib\gdi32.lib
      includelib \masm32\lib\kernel32.lib

.data
szDisplayName db "Test",0
szClassName   db "Test_Class",0
szTest                    db "I don't want to feel guilty if my programs"
                          db " don't start with :",13,10
                          db " Start:",13,10
                          db "            invoke  GetModuleHandle,NULL",10,13
                          db "            mov     hInstance, eax",10,13
                          db "            invoke  WinMain,eax,0,0,0",10,13
                          db "            invoke  ExitProcess,eax",10,13
                          db " END Start",0
szConfirm                 db "Please Confirm Exit",0
wc     WNDCLASSEX  <sizeof WNDCLASSEX, CS_HREDRAW or CS_VREDRAW or CS_BYTEALIGNWINDOW, \
                    offset WndProc, 0, 0,0,0,0,COLOR_WINDOW +1, 0, szClassName, 0>
.data?
hWnd           dd  ?
dStack         dd  ?   
ps             PAINTSTRUCT  <?>
rect           RECT  <?>

.code
start:
mov         esi, 0FFFF0000h
mov         dStack, esp             ; save the stack
and         esi, $        ; esi->instance handle
invoke      LoadIcon, 0, IDI_APPLICATION
mov         wc.hIcon, eax
invoke      LoadCursor, 0,IDC_ARROW
mov         wc.hCursor, eax
mov         wc.hInstance, esi   ; esi->instance handle
invoke      RegisterClassEx, addr wc
invoke      CreateWindowEx, WS_EX_OVERLAPPEDWINDOW,
                 addr szClassName,
                 addr szDisplayName,
                 WS_OVERLAPPEDWINDOW or WS_VISIBLE,
                 100,100,500,350,
                 0, 0, esi, 0   ; esi->instance handle
;...............................................;
WndProc:                   
               cmp esp, dStack       
               je  WinGetMessage   
WinMain:                   
               mov  eax, [esp+2*4]      ; eax->uMsg
               mov  esi, [esp+1*4]      ; esi->hwnd
               cmp  eax,WM_CLOSE 
               je   OnDestroy       
               cmp  eax, WM_PAINT 
               je   OnPaint           
               cmp  eax, WM_CREATE 
               jne  DefWindowProc  ;  call API
;...................................................................;
OnCreate:                                     
         mov   hWnd, esi                           
         jmp    DefWindowProc                     
;...................................................................;
OnPaint:                                         
        invoke BeginPaint, esi, ADDR ps
        mov    edi, eax                        ; edi->hDC
        invoke GetClientRect, esi, addr rect
        invoke DrawText, edi, addr szTest,-1,\ ; edi->hDC
                              addr rect, DT_VCENTER     
        invoke EndPaint, esi, addr ps         
        jmp    DefWindowProc     ;  call API
;...................................................................;
OnDestroy:                                 
              invoke MessageBox, esi, \     
              addr szConfirm, \                 
              addr szDisplayName,\         
              MB_YESNO                       
              cmp  eax, IDNO     
              jne    ExitProcess              ; Exit
;...................................................................;
             mov  esp, dStack       
WinGetMessage:           
             lea eax, [esp-4*4]           
             mov [esp-5*4], offset WndProc          ; return address -> WndProc
             mov dword ptr [esp-6*4], 0             ; zeros are parameters
             mov dword ptr [esp-7*4], 0             ; of GetMessage
             mov dword ptr [esp-8*4], 0                 
             mov [esp-9*4], eax                     ; eax-> offset MSG struct
             mov [esp-10*4], offset WinMain         ; return-> WinMain
             sub  esp, 10*4                               
             jmp  GetMessage                        ; call API
;...................................................................;
end  start


zemtex

Anyway, I always use GetQueueStatus for quick hint if there is messages available before i pass the execution to the slow (5 push) peekmessage function. GetQueueStatus only has 1 push and it checks a few flags. If the message loop runs 1 million times in 10 ms, that means peekmessage is pushing and popping 5 million times (Thats alot of memory reading) Microsoft SDK also mentions that getqueuestatus is the fastest possible way to check if there are available messages.
I have been puzzling with lego bricks all my life. I know how to do this. When Peter, at age 6 is competing with me, I find it extremely neccessary to show him that I can puzzle bricks better than him, because he is so damn talented that all that is called rational has gone haywire.

qWord

zemtex,
you are worrying about about some push's while calling API-functions that end up in an syscall :P
FPU in a trice: SmplMath
It's that simple!

zemtex

Quote from: qWord on September 05, 2010, 11:03:50 AM
zemtex,
you are worrying about about some push's while calling API-functions that end up in an syscall :P

Microsoft says its faster, you reduce overhead in the message loop. if you produce inputs, one every 50,000 iterations, lets say by a keystroke, you would have avvoided peekmessage for 50,000 iterations. peekmessage pushes 20 bytes of data per iteration and performs a long execution afterwards to filter messages, remove unwanted messages, fill inn the msg structure. When you use getqueuestatus you push 4 bytes of data and the system checks a few flags and returns. Thats much faster.

Syscall or no syscall. If the syscall have a delay of 10 ms, that doesnt matter because you would have to substitute the delay of the message function and you get a lower overhead no matter how intensive the syscall is. This is simple math.  :U
I have been puzzling with lego bricks all my life. I know how to do this. When Peter, at age 6 is competing with me, I find it extremely neccessary to show him that I can puzzle bricks better than him, because he is so damn talented that all that is called rational has gone haywire.

MichaelW

Quote from: zemtex on September 06, 2010, 10:55:19 AM
Microsoft says its faster, you reduce overhead in the message loop. if you produce inputs, one every 50,000 iterations, lets say by a keystroke, you would have avvoided peekmessage for 50,000 iterations. peekmessage pushes 20 bytes of data per iteration and performs a long execution afterwards to filter messages, remove unwanted messages, fill inn the msg structure. When you use getqueuestatus you push 4 bytes of data and the system checks a few flags and returns. Thats much faster.

Syscall or no syscall. If the syscall have a delay of 10 ms, that doesnt matter because you would have to substitute the delay of the message function and you get a lower overhead no matter how intensive the syscall is. This is simple math.  :U

I was going to point out that when there are messages a message loop that calls GetQueueStatus and PeekMessage actually has higher overhead, but now that I test, at least on my Windows 2000 system GetQueueStatus is much slower than PeekMessage even when there are no messages. Specifically, a million loops of the message loop took ~1140ms when the loop called GetQueueStatus(QS_ALLINPUT), or ~140ms when it did not.


;==============================================================================
; Build as a console app.
;==============================================================================

;----------------------------------------------------------------------------
; This macro more or less duplicates the functionality of the BASIC language
; TIMER function, returning the elapsed seconds since the system was started
; as a floating-point value at the top of the FPU stack. The elapsed seconds
; are derived from the high-resolution performance counter, so the effective
; resolution is on the order of a few microseconds.
;----------------------------------------------------------------------------

timer MACRO
    IFNDEF _timer_pc_frequency_
        .data
            align 8
            _timer_pc_frequency_      dq 0
            _timer_pc_count_          dq 0
            _timer_elapsed_seconds_   dq 0
            _timer_initialized_       dd 0
        .code
    ENDIF
    .IF _timer_initialized_ == 0
        invoke QueryPerformanceFrequency, ADDR _timer_pc_frequency_
        inc _timer_initialized_
    .ENDIF
    invoke QueryPerformanceCounter, ADDR _timer_pc_count_
    fild _timer_pc_count_
    fild _timer_pc_frequency_
    fdiv
    fstp _timer_elapsed_seconds_
    EXITM <_timer_elapsed_seconds_>
ENDM

;==============================================================================
    include \masm32\include\masm32rt.inc
;==============================================================================
    .data
        hInstance dd    0
        hDlg      dd    0
        loopcount dd    0
        pmcount   dd    0
        gqscount  dd    0
        time      REAL8 ?
        seconds   REAL8 ?
        msg       MSG   <>
    .code
;==============================================================================

DialogProc proc hwndDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD

    SWITCH uMsg

      CASE WM_CLOSE

        invoke DestroyWindow, hwndDlg

      CASE WM_DESTROY

        invoke PostQuitMessage, NULL

    ENDSW

    xor eax, eax
    ret

DialogProc endp

;==============================================================================
start:
;==============================================================================
    invoke GetModuleHandle, NULL
    mov hInstance, eax
    Dialog 0, \
           "FixedSys",11, \
           WS_VISIBLE or WS_OVERLAPPED or WS_SYSMENU or DS_CENTER, \
           0, \
           0,0,50,40, \
           1024
    CallModelessDialog hInstance, 0, DialogProc, NULL
    mov hDlg, eax
    fld timer()
    fstp time
  msgLoop:
    inc loopcount
    .IF loopcount > 1000000
        ;--------------------------------------------------------
        ; Calc and display the elapsed seconds for 1000000loops,
        ; along with the message counts.
        ;--------------------------------------------------------
        fld time
        fld timer()
        fst time
        fsubr
        fstp seconds
        invoke crt_printf, cfm$("%f\t%d\t%d\n"), seconds, pmcount, gqscount
        mov loopcount, 0
        mov pmcount, 0
        mov gqscount, 0
    .ENDIF

    IncludeGetQueueStatus equ 1

IFDEF IncludeGetQueueStatus
    invoke GetQueueStatus, QS_ALLINPUT
    .IF eax
        inc gqscount
ENDIF
        invoke PeekMessage, ADDR msg, NULL, 0, 0, PM_REMOVE
        .IF eax != 0
            inc pmcount
            .IF msg.message == WM_QUIT
                invoke ExitProcess, 0
            .ENDIF
            invoke TranslateMessage, ADDR msg
            invoke DispatchMessage, ADDR msg
        .ENDIF
IFDEF IncludeGetQueueStatus
    .ENDIF
ENDIF
    jmp msgLoop
;==============================================================================
end start


eschew obfuscation

zemtex

Quote from: MichaelW on September 06, 2010, 06:16:35 PM
I was going to point out that when there are messages a message loop that calls GetQueueStatus and PeekMessage actually has higher overhead, but now that I test, at least on my Windows 2000 system GetQueueStatus is much slower than PeekMessage even when there are no messages. Specifically, a million loops of the message loop took ~1140ms when the loop called GetQueueStatus(QS_ALLINPUT), or ~140ms when it did not.


All I can say is microsofts own words: "GetQueueStatus returns an array of flags that indicates the types of messages in the queue; using it is the fastest way to discover whether the queue contains any messages."

And also, keep in mind that if your message queue is always filled up, you are basically running getqueuestatus and peekmessage both at the same time per message. The getqueuestatus is really only fastest if it is used a single time or if there are few messages available. You have to weight it and see which option is better based on what program you have.

If your program is always having messages it just might be that you would do better with dropping getqueuestatus entirely.  :U

EDIT: It might be that what microsoft means by "fastest way" is that its the most easy function call to "USE", and that its not actualyl the fastest in execution. I can't really tell. The timing was interesting though. Try to run it a few more times and see if it is consistent.
I have been puzzling with lego bricks all my life. I know how to do this. When Peter, at age 6 is competing with me, I find it extremely neccessary to show him that I can puzzle bricks better than him, because he is so damn talented that all that is called rational has gone haywire.

MichaelW

Test it. The speed difference exists whether there are messages or not. Unless I intentionally trigger messages by pressing keys when the window has the focus, moving the mouse within the window, moving the window, etc, then there are no messages. I would be interested to see the timings for Windows XP and later.
eschew obfuscation

oex

Windows XP:

IncludeGetQueueStatus equ 1
0.282236        14      1
0.284462        21      2
0.291163        11      1
0.281626        0       0
0.276215        0       0
0.271831        0       0
0.285561        0       0
0.275448        0       0
0.277165        0       0
0.275120        0       0
0.281987        0       0
0.277742        0       0
0.279168        0       0
0.282788        0       0
0.274141        0       0

IncludeGetQueueStatus equ 0
0.297155        10      11
0.269335        24      24
0.280158        12      12
0.258623        0       0
0.260007        0       0
0.253857        0       0
0.267360        0       0
0.271125        0       0
0.252812        0       0
0.265616        0       0
0.271749        0       0
0.255088        0       0
0.260941        0       0
0.260837        0       0
0.271073        0       0
We are all of us insane, just to varying degrees and intelligently balanced through networking

http://www.hereford.tv

zemtex

On my computer with Windows 7 the getqueuestatus runs with numbers like 0.22 most of the time, and peekmessage runs with 0.23-24 most of the time when there is 0 messages available in the queue.

When there was the same amount messages available, getqueuestatus ran with 0.231 and peekmessage run slightly slower at 0.239

I DONT KNOW GUYS, what do you think. Should this method be expelled or what? You guys deside.  :cheekygreen:
I have been puzzling with lego bricks all my life. I know how to do this. When Peter, at age 6 is competing with me, I find it extremely neccessary to show him that I can puzzle bricks better than him, because he is so damn talented that all that is called rational has gone haywire.

MichaelW

I see now that I should have used a different way of controlling the conditional assembly. Because I'm testing with IFDEF, the GetQueueStatus code will be included if IncludeGetQueueStatus is defined, whatever the value assigned to it. I intended that the entire statement be commented out to remove the GetQueueStatus code. To use it the way that my equate value suggests it should be used, the:

IFDEF IncludeGetQueueStatus

statement should be:

IF IncludeGetQueueStatus EQ 1
eschew obfuscation

zemtex

You would also have to remove peekmessage to time both of them individually. You include peekmessage when getqueuestatus runs. You increase gqscount but the incresage will be wrong because it waits until peekmessage increases too, the timing will be wrong.
I have been puzzling with lego bricks all my life. I know how to do this. When Peter, at age 6 is competing with me, I find it extremely neccessary to show him that I can puzzle bricks better than him, because he is so damn talented that all that is called rational has gone haywire.