The MASM Forum Archive 2004 to 2012

General Forums => The Laboratory => Topic started by: CoR on January 31, 2007, 06:19:43 AM

Title: What's the theory behind the "Message Pump"?
Post by: CoR on January 31, 2007, 06:19:43 AM
Search didn't gave any relevant result and I am still puzzled why all tutorial examples has the same message pump?
I have two major questions here. And both are logical issues.

So, here's the "message loop" as presented in all tutorial examples I have laid my eyes on:
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
LOCAL wc:WNDCLASSEX
LOCAL msg:MSG
LOCAL hwnd:HWND
...
                .WHILE TRUE
                INVOKE GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)
                INVOKE TranslateMessage, ADDR msg
                INVOKE DispatchMessage, ADDR msg
.ENDW
mov     eax,msg.wParam
ret
WinMain endp


First question:
Is there any specific reason to put wc, hwnd and msg as LOCAL variable on stack?
Does this makes application faster, more stable or anyway better? Why is it so good to use stack?
Because this works fine:
.DATA?
msg MSG <>
hwnd dd ?

Is there any benefit to use LOCAL to allocate those variables and structures? And is there any benefit if you avoid using stack?

Second question:
What is the reason to use ADDR msg in every INVOKE?
Does WinXP somehow moves msg STRUCT without our knowledge so we have to use ADDR always?
Because this also works:

.DATA?
msg MSG <>
msg1 dd ?

.CODE
lea eax,msg
mov msg1,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
...
                .WHILE TRUE
                INVOKE GetMessage, msg1,NULL,0,0
                .BREAK .IF (!eax)
                INVOKE TranslateMessage, msg1
                INVOKE DispatchMessage,  msg1
.ENDW
mov     eax,msg.wParam
ret
WinMain endp


Am I right to say that INVOKE DispatchMessage, ADDR msg will always assemble as:
lea eax,msg
push eax
call DispatchMessage


And INVOKE DispatchMessage,  msg1   will assmeble as:
push msg1
call DispatchMessage


If this works and if it looks 'nicer', where is the catch? I see that first Invoke is almost exactly like C/C++. And second is "optimized" and more like asm. So what's wrong with it in a long run?
I am newbie, but I would love to learn about theory of asm and Win OS also :)
Especially if I'll learn WHY are some things like they are  :cheekygreen:
Title: Re: What's the theory behind the "Message Pump"?
Post by: TNick on January 31, 2007, 08:43:14 AM
Quote from: CoR on January 31, 2007, 06:19:43 AM
First question:
Is there any specific reason to put wc, hwnd and msg as LOCAL variable on stack?
...
Is there any benefit to use LOCAL to allocate those variables and structures? And is there any benefit if you avoid using stack?

This is a long discussion :). What is the benefit of using local variables?
Have a look at this dummy function:

MyStruct STRUCT
  FirstMember:DWORD
  SecondMember:DWORD
MyStruct ENDS

SomeDummy  PROC
LOCAL FirstVar:DWORD
LOCAL FirstStruct:MyStruct
    mov  FirstVar, 10
    mov  FirstStruct.SecondMember, eax
ret
SomeDummy  ENDP


This will generate the following code:

PUSH EBP ;preserve ebp in the stack
MOV EBP,ESP;store the stack point in ebp
ADD ESP,-0C;make place for our local variables in the stack (4 = SIZEOF FirstVar, 8 = SIZEOF MyStruct)
MOV [EBP-4],0A   ;code in our proc
MOV [EBP-8],EAX
LEAVE   ;restore esp and ebp
;mov esp, ebp
;pop ebp
RET ;return


If you use global variables (in .DATA or .DATA?), you will have 12 bytes in memory all the time when your exe is running.
The benefit is that local variables are "allocated" at run time, so your exe size will be smaller.
On the other hand, using globals will improve the speed of that function, because there's no need for

PUSH EBP
MOV EBP,ESP
ADD ESP,-0C
.
.
.
LEAVE
RET

and MOV [EBP-8],EAX will be replaced with something like mov [400012], eax, which may improve the speed, also (not shure about this one).

Quote from: CoR on January 31, 2007, 06:19:43 AM
Second question:
What is the reason to use ADDR msg in every INVOKE?
Does WinXP somehow moves msg STRUCT without our knowledge so we have to use ADDR always?
ADDR doesn't have anything to do with the version of windows that you are running. ADDR is a macro that can be used only inside INVOKE, and it computes the offset - in memory - for it's operand.
- MyVar in .DATA, .DATA, .CONST section
PUSH OFFSET MyVar
lea eax, MyVar
push eax
will work, but there's 2 instructions,and there's no need for that.
- MyVar is a local variable
lea eax, MyVar ;lea eax, [ebp-4]
push eax


As a result, your code may become:
.DATA?
msg MSG <>
msg1 dd ?

.CODE
mov msg1, OFFSET msg

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
...
                .WHILE TRUE
                INVOKE GetMessage, msg1,NULL,0,0
                .BREAK .IF (!eax)
                INVOKE TranslateMessage, msg1
                INVOKE DispatchMessage,  msg1
.ENDW
mov     eax,msg.wParam
ret
WinMain endp

but you may note that you don't need msg1 (address of msg is a constant that you know)

INVOKE is just a macro like any other macro. It is designed to help you write mode readable code. You may use it or not, it's your choice. Let's take a look at some examples:
INVOKE MyFunction, SomeVar
is same as
push SomeVar
call MyFunction

I see no need to use the longer form.

INVOKE MyFunction, SomeVar, ADDR SomeVar2, ADDR SomeStruct1
is same as
push OFFSET SomeStruct1 ;or lea eax, ... for locals
push OFFSET SomeVar2; or lea eax, ... for locals
push SomeVar
call MyFunction


here, you may do a little speed increase, if both SomeVar2 and SomeStruct1 are locals
using invoke will generate this code
lea eax, SomeStruct1
push eax
lea eax, SomeVar2
push eax


wich you may do like this
lea eax, SomeStruct1
lea ecx, SomeVar2
push eax
push ecx


I use invoke in calls to functions with many parameters, when the values that I want to pass are in registers, are constants (offsets too)
for example (presuming that esi points to the path and name):
INVOKE CreateFile,ADDR FileName,GENERIC_READ,FILE_SHARE_DELETE,NULL,CREATE_NEW,FILE_ATTRIBUTE_ARCHIVE
to
sub esp, 6*4 ; six parameters, 4 bytes each
mov dword ptr [esp], esi
mov dword ptr [esp+4],GENERIC_READ
mov dword ptr [esp+8], FILE_SHARE_DELETE
.
.
.

but, I have to admit, the speed increase is not very big.

HTH

Regards,
Nick





Title: Re: What's the theory behind the "Message Pump"?
Post by: hutch-- on January 31, 2007, 09:23:10 AM
CoR,

Your question is a good one, the message loop is fundamental to a Windows GUI application and it works in an interesting way. It is fundamentally a polling loop but with an important difference, it simply stops until GetMessage() returns another message and this is very efficient in terms of processor usage. An app that is not receiving messages uses no processor power at all.

To get it all up and running in a GUI app, you first load a WNDCLASSEX structure wih all of the arguments required to create a window including the address of the message handling procedure, nominally caled the "WndProc". You register the class with Windows then with that class you create a window using CreateWindowEx(). Before the CreateWindowEx() function returns it goes to the "WndProc" once to handle the "WM_CREATE" message then returns with the identifier that is normally called a window handle. You use this handle to interact with the window after it is created.

After any other stuff that you may want to start like menus or similar, you show the window with ShowWindow() then fall into the message loop. The action with the message loop continues until GetMessage() returns ZERO which is normally a programmatically controlled event. Otherwise it polls the operating system with GetMessage() and if nothing is available, it simply sits there until another message becomes available.

Some messages are sent directly by the OS to the WndProc but typically keyboard and a few other messages are returned to the message loop where they can be processed first then dispatched to the WndProc. You will notice that an application never calls a "WndProc". It is only ever called by the OS on the basis of a message needing to be sent to the window. The "WndProc" is called a "callback because of this and it is a reasonably typical technique in Windows for various types of Windows including the ordinary controls like buttons and so on.

A "WndProc" and a control subclass are almost identical except that a control already has a default "WndProc" so when you want to process messages for a control you have to intercept the message stream and then return to the default handler.
Title: Re: What's the theory behind the "Message Pump"?
Post by: TomRiddle on January 31, 2007, 12:37:45 PM
ICZ's tutorials(I ain't gonna bother butchering his name) mostly use a WinMain procedure, if you were asking about the point of it....I quite honestly can't see one.

On the other hand I can see why he did it, in another post someone referred to their program code as "code: main", this rational is a fall-out from C users.  Me, I just skip the whole WinMain procedure and go directly to the code it uses, basically removing as much LOCAL data as possible in favor of either static(.DATA) or dynamic(.DATA?) data.  Even my WNDCLASSEX structures tend to be put in the .Data section so I don't have to bother writing commands just to update data that never changes(I.E. SizeOf WNDCLASSEX).

Not sure if that's what you were asking, but it's how I do things.
Title: Re: What's the theory behind the "Message Pump"?
Post by: lingo on January 31, 2007, 03:06:07 PM
Why not a Message Loop with MMX: :lol

.data
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

End Start


Regards,
Lingo
Title: Re: What's the theory behind the "Message Pump"?
Post by: CoR on February 01, 2007, 07:17:39 PM
@TomRiddle:
I do not know why, buy I think I am developing a fetish for .DATA?. Lingo and you have some mean wc in .DATA. I like it :)

Another question, does both .DATA and .DATA? have same access time or not? I know it's all the same memory, but does each one have advantage over the other? Eg. retains longer in cache or some weird CPU benefits in speed or addressing or something.

@hutch:
I didn't know that message loop loops (pun:) ONLY if there's message to process. It makes sense optimizing it only if application has some heavy duty message cycle. Like games!

@TNick:
Thanks for the LOCAL example ;) I fully understand it!
But I lost you in second part.

.DATA?
msg MSG <>

.CODE
...
WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD
...
                .WHILE TRUE
                INVOKE GetMessage, msg,NULL,0,0
                .BREAK .IF (!eax)
                INVOKE TranslateMessage, msg
                INVOKE DispatchMessage,  msg
.ENDW
mov     eax,msg.wParam
ret
WinMain endp

This code does not work. It assembles without error. But when I start it, window closes immediately. Did I made some mistake or I do need to put mov msg1, OFFSET msg even msg is in .DATA?
Title: Little help mr. wizard
Post by: CoR on February 01, 2007, 10:55:25 PM
I have some trouble understanding wc in .DATA Little help is needed here (http://www.masm32.com/board/index.php?topic=6482.0)
Title: Re: What's the theory behind the "Message Pump"?
Post by: hutch-- on February 01, 2007, 11:11:50 PM
CoR,

With the WNDCLASSEX structure, it is used to hold the arguments for the RegisterClassEx() call that follows it. This is a common technique in windows to load a structure then call a function passing the address of the structure instead of a long list of parameters. You can create the structure either locally on the stack or if its only a demo you can put it in the .DATA section with preloaded values. There is no advantage in the .DATA section method.

With the message loop, its operation is defined by the operating system so the GetMessage(), TranslateMessage() if used and DispatchMessage() calls are not subject to optimisation but if you are processing code from the message loop it is subject to normal code optimisation techniques.

Games comonly hog a large amount of processor time as they do not have to worry about processor slice sharing so they will tend to use the message loop more like an old fashioned polling loop rather than a Windows GUI app that must co-exist with other running applications.
Title: wc.
Post by: CoR on February 02, 2007, 12:09:47 AM
Hutch, man, now you are scaring me  :wink  What do you mean by "...if its only a demo you can put it in the .DATA section with preloaded values" ?

Will wc. defined in .DATA work same as wc. defined as LOCAL or not? On every comp with Win OS?

What about lingo's wc defined as wc dd param1, param2, ...

You said that there's no advantage to put wc. in .DATA. Is there any downfall or reason not to do things like that?
edit: Same question for "to use or not to use" WinMain? Wrom your experience  is there any downfall of writing direct code and skipping WinMain and stack allocatons?

To me it all looks the same. And I deduce that that code should be 100% same in computers eyes. And we all know how deduction can be wrong :lol  So better to be save than sorry  :wink

I am trying to make some mental picture of what I can and what I can not do with asm. Luckily I decided to make little project and learn things as project develops. Otherwise I would be more than lost. I mean, I am pretty lost right now, but at least I know where am I going and what my next step will be  :bg
Title: Re: What's the theory behind the "Message Pump"?
Post by: TNick on February 02, 2007, 09:17:16 AM
Huge mistake there ... Wasn't careful! Sorry!
This is a complete example of what I am using to start a new project.

[EDIT]
Now, about your last post...

I had explained in my first post what is the difference between a LOCAL variable and one in the data section. Major difference: you can give a initial value for the one in data section. A variable located in the stack will have an random initial value (depending on what vase on the stack before at that position). From this point of view, it's similar to variables in .DATA? section. Second difference is the reference method. When you write
mov  eax,  Var01
and Var01 is a local one, the generated code will look like this:
mov  eax,  [EBP--4]
If it's a Global one (in .DATA section, in .DATA? section or even in .CONST section), it will look like this
mov  eax,  [04040010]
As I said before, I'm not shore which method is faster.

About using or not WinMain, as you can see in the attached template, I don't use it and I didn't use it at all. Icz tutorial uses this form to make it look like a C program, I think. WinMain is not a callback function and, therefore, Windows doesn't care how you're creating your windows. WndProc, on the other hand, can have any name, but it has to have that particular number of parameters, because it's a callback function.

[/EDIT]

Regards,
Nick

[attachment deleted by admin]
Title: Re: What's the theory behind the "Message Pump"?
Post by: sinsi on February 02, 2007, 09:42:58 AM
Here's my version of registering a window:

    sub ebx,ebx
    mov esi,32512

    push ebx
    call GetModuleHandle
    mov hinst,eax
    mov edi,eax

    push ebx                ;hIconSm
    push OFFSET frmclass    ;lpszClassName
    push ebx                ;lpszMenuName
    push 1                  ;hbrBackground
    push esi
    push ebx
    call LoadCursor
    push eax                ;hCursor
    push esi
    push ebx
    call LoadIcon
    push eax                ;hIcon
    push edi                ;hInstance
    push ebx                ;cbWndExtra
    push ebx                ;cbClsExtra
    push OFFSET frmproc     ;lpfnWndProc
    push 3                  ;style
    push 48                 ;cbSize
    push esp
    call RegisterClassEx
    add esp,48
Title: Re: What's the theory behind the "Message Pump"?
Post by: zooba on February 02, 2007, 11:49:04 AM
I'm going back to the original post because there seems to be little in the way of actual answers in the follow ups. TNick gave a good summary of the differences between .DATA and LOCAL and Hutch gave some good general info on message loops so between them you should be right. I'll give my take on your direct, original, questions.

Quote from: CoR on January 31, 2007, 06:19:43 AM
I am still puzzled why all tutorial examples has the same message pump?

Because Microsoft has suggested this format as the best way to write it. Since they're the ones writing the APIs themselves (and hence constantly benchmarking and testing them), they probably know best.

Quote from: CoR on January 31, 2007, 06:19:43 AM
First question:
Is there any specific reason to put wc, hwnd and msg as LOCAL variable on stack?
Does this makes application faster, more stable or anyway better? Why is it so good to use stack?
...
Is there any benefit to use LOCAL to allocate those variables and structures? And is there any benefit if you avoid using stack?

Yes, though in most cases it has no effect. Hutch mentioned that using the .DATA section will work fine for demos and this is true. However, if you are writing a complicated program involving multiple threads each with their own message loop (aka pump), you will need local variables to ensure that the threads don't overwrite each other's data. The .DATA/.DATA? section is common to all threads belonging to the same process (note that you can't have two message loops on the same thread since each will block).

In terms of speed, the APIs being called involve transistions to kernel mode. There's absolutely nothing you can do to slow these down any more. As soon as GUI is involved, performance becomes largely irrelevant. (Disclaimer: You can obviously block the thread somewhere else waiting for something, ie. an event/semaphore/mutex/file copy/long operation/etc., however, this has nothing to do with where your message loop variables are stored.)

Quote from: CoR on January 31, 2007, 06:19:43 AM
Second question:
What is the reason to use ADDR msg in every INVOKE?
Does WinXP somehow moves msg STRUCT without our knowledge so we have to use ADDR always?
...
If this works and if it looks 'nicer', where is the catch? I see that first Invoke is almost exactly like C/C++. And second is "optimized" and more like asm. So what's wrong with it in a long run?

There's no catch. You can get the address of the structure at the start, store it in another variable (or even ESI/EDI/EBX, which are preserved during API calls) and use that instead. In the long run, the net gain is zero. The API calls are the bottlenecks and there's nothing you can do to speed them up. (In reality, the biggest bottleneck in GUI programming is the user, and NOTHING can speed them up :bg ) If you put the structures in the .DATA/.DATA? section, you can use OFFSET which will assemble to push DWORD PTR [00410000h] but again, there is nothing to be gained by this when compared to the other performance issues. In general, I believe most people will find ADDR msg more readable than pMsg or something similar. (In general, I believe most people never read the code inside a message loop anyway.)

Quote from: CoR on January 31, 2007, 06:19:43 AM
I am newbie, but I would love to learn about theory of asm and Win OS also :)
Especially if I'll learn WHY are some things like they are  :cheekygreen:

Good. Do you read Raymond Chen's (http://blogs.msdn.com/oldnewthing) blog? He very often writes about the history of Win32, compatibility issues which MS deal with, reasons for why things are as they are and has been known to flex some pretty awesome debugging muscles (even in assembly).

Cheers,

Zooba :U
Title: Re: What's the theory behind the "Message Pump"?
Post by: CoR on February 02, 2007, 07:52:29 PM
Ok Nic, I think I've got full understanding of local and global variables now. On first glance it looks like C but when you see how LOCAL and .DATA are made... ASM really makes a difference.
Though I'll have to read this thread from time to time. Just to plant it in my mind firmly ;) Thanks man :)

So WinMain or not it'll work nice everywhere, any time. That's nice to hear. Zooba, thanks for untangling a lot of my noob problems.

And as far as wc. goes is it OK to say that there's more safety if U allocate it as LOCAL. And nothing else?

p.s. Raymond Chen's blog is nice one  :wink
Title: Re: What's the theory behind the "Message Pump"?
Post by: zooba on February 02, 2007, 11:49:03 PM
Quote from: CoR on February 02, 2007, 07:52:29 PM
And as far as wc. goes is it OK to say that there's more safety if U allocate it as LOCAL. And nothing else?

Nothing else of note. You also avoid the problems associated with global variables, you get to use the variable names in other places without breaking your message loop, the size of your executable is smaller (by a few bytes, but some people care about those things) if you don't use the .DATA section (.DATA? is okay) and you are able to use the same message loop for different threads (because each thread has their own stack, but there's only one .DATA/.DATA? section for all threads).

Cheers,

Zooba :U
Title: Re: What's the theory behind the "Message Pump"?
Post by: TomRiddle on February 04, 2007, 09:33:41 AM
Something I didn't like much about the tutorials was that he used a WinMain procedure, I just usually skip it in favor of having it all in the main Code region.

Here, btw, is my message pump

L_StartLoop:
  Invoke GetMessage, Addr St_Msg, Null, 0, 0
  JumpIfRegisterIsZero Eax, L_ExitLoop
  Invoke TranslateMessage, Addr St_Msg
  Invoke DispatchMessage, Addr St_Msg
  Jump L_StartLoop
L_ExitLoop:


[EDIT]
On a separate note, re-reading the thread, I get this view:

QuoteLate in the afternoon, CoR is driving to a friends house when he suddenly notices he is lost.  Lost?  Whatever can he do, when off in the distance like a beacon of hope looms a sign "Masm32 Forum".  Pulling to the side of the road a grizzly man in a full length beard appears.

"What can we do for you stranger?", he says adjusting some un-named tool in his hand.

CoR replies, "Well, I was on my way to a friends house, but I got kinda lost, was hoping you could help me."

"Lost eh?", says another patron of the old time Masm32 station, "Well now, let's get you found again."

"Whose lost?", yells a man from behind the pumps.

Words are a great thing aren't they. :eek
[/EDIT]
Title: Re: What's the theory behind the "Message Pump"?
Post by: CoR on February 04, 2007, 11:54:40 AM
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.
Title: Re: What's the theory behind the "Message Pump"?
Post by: lingo on February 04, 2007, 05:42:08 PM
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
Title: Re: What's the theory behind the "Message Pump"?
Post by: Rockoon on February 05, 2007, 05:09:07 PM
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)


Title: Re: What's the theory behind the "Message Pump"?
Post by: Seb on February 10, 2007, 11:07:42 PM
lingo, is it possible to get the full source code of your MMX message looper? Some parts appear to be missing.

Regards,
Seb
Title: Re: What's the theory behind the "Message Pump"?
Post by: hutch-- on February 10, 2007, 11:40:25 PM
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
Title: Re: What's the theory behind the "Message Pump"?
Post by: lingo on February 11, 2007, 05:21:27 AM
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]
Title: Re: What's the theory behind the "Message Pump"?
Post by: Seb on February 11, 2007, 12:00:04 PM
Thank you very much, lingo! :U :clap:
Title: Re: What's the theory behind the "Message Pump"?
Post by: hutch-- on February 12, 2007, 02:46:36 AM
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]
Title: Re: What's the theory behind the "Message Pump"?
Post by: daydreamer on February 12, 2007, 05:38:35 AM
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
Title: Re: What's the theory behind the "Message Pump"?
Post by: hutch-- on February 12, 2007, 07:37:29 AM
magnus,

You are right but I warned that it was useless. With 3 API calls its a "who cares" with a message loop.  :bg
Title: Re: What's the theory behind the "Message Pump"?
Post by: u on February 12, 2007, 10:27:42 AM
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
Title: Re: What's the theory behind the "Message Pump"?
Post by: u on February 12, 2007, 12:28:23 PM
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) .

(http://img368.imageshack.us/img368/2797/zzztest2hm5.png)

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
Title: Re: What's the theory behind the "Message Pump"?
Post by: 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.
Title: Re: What's the theory behind the "Message Pump"?
Post by: daydreamer on February 12, 2007, 01:47:31 PM
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
Title: Re: What's the theory behind the "Message Pump"?
Post by: ic2 on February 12, 2007, 11:58:02 PM
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 ?
Title: Re: What's the theory behind the "Message Pump"?
Post by: u on February 13, 2007, 05:27:39 AM
ic2, take note of the stack - the parameters are already pushed (hWnd,msg,wParam,lParam), the return address-value points to inside the code of DispatchMessage - so it's better than pushing the same parameters and doing and extra "ret".
DefWindowProc is a Win32 API function.
Title: Re: What's the theory behind the "Message Pump"?
Post by: ic2 on February 13, 2007, 04:47:19 PM
Thanks Ultrano for explaining the un-seen.

I playing with it to understand what's going on with Olly.  But it crash after Olly execute the window "only" when I run the mouse over the window tooltips buttons displaying Close, Maximize, Minimize it crash Windows98 completely.  I don't have Olly experience.  Maybe that what happen with any program after Olly execute it.  Maybe that extra "ret" is needed.  If not,  I just thought I should mention it.

Anyway Olly do show  DefWindowProc being used even when there is no label for it. But I think I see what you mean and will be exploring it all today.  WoW ... this is going to be really interesting for my knowledge of ASM. When I come out of this i should really finally be on the ball a whole lot better  :)  Not to take away from others people efforts around here but  Lingo style is    ....  Awesome!!!   .... Thanks CoR
Title: Re: What's the theory behind the "Message Pump"?
Post by: u on February 13, 2007, 05:32:19 PM
Or maybe it's just an effect of the lack of "emms" ? I don't have a win98 to test it on - could you try the same with a compilation, where there's emms after the group of mmx instructions?
Title: Re: What's the theory behind the "Message Pump"?
Post by: ic2 on February 13, 2007, 06:46:31 PM
Quotecould you try the same with a compilation, where there's emms after the group of mmx instructions?

I don't have a clue of where to start other than starting all over  to learn about dos and work my way up to win32 asm as I should had done in the first place.  That show you what i really know . ..

Anyway, if it's don't take much time for you to put it together I can't test it on Win98se and Win95b and post the results in minutes .  It will be tested on a Intel Pentium(r) II, 128MB machine.  If i see it than I have a idea of how to use it and  modify it if i have a problem. Shame on me, all I am is a code monkey.  Not a true coder in my mind as of yet... but i can usually modify like a pro when needed.
Title: Re: What's the theory behind the "Message Pump"?
Post by: ic2 on February 13, 2007, 08:11:15 PM
I did a search here to get an idea of how to use EMMS and see it's as simple as placing the line below the MMX code like you said.  I never played with MMX code before and had no idea of what you meant by technical way of speaking.  I thought it was a major operation...

Anyway, I put the line under "both" blocks of MMX code and now Olly don't  crash  anymore  at the mouseover but it will now crash if  i click the close button.  What an amazing improvement.  Hummmm going to try to find the other correct places to use EMMS to eliminate the close crash if possible...  I may need more guidance, but i will make the effort to read about EMMS and the study the code that i found using it... in a few minutes...