What's the theory behind PROC and all functions in masm?

Started by CoR, March 06, 2007, 10:18:05 PM

Previous topic - Next topic

CoR

If I remember correctly when you make function in C/C++ that function is allocated on stack, uses local variables allocated on stack, etc. When it's finished it's simply deleted from stack. The end. You HAVE to pass result in some global var or to pass it as value or pointer. If you fail to do that result will be lost as stack is filled with new data. Forever. I think I got this right.

But functions seems to behave different in MASM! Why? 

For instance, why this function works? Because RegisterClassEx passes no parameters or because once declared WNDCLASSEX is no longer needed if we use fn RegisterClassEx or something else...
RegisterWinClass proc lpWndProc:DWORD, lpClassName:DWORD,
                      Icon:DWORD, Cursor:DWORD, bColor:DWORD

    LOCAL wc:WNDCLASSEX

    mov wc.cbSize,         sizeof WNDCLASSEX
    mov wc.style,          CS_BYTEALIGNCLIENT or \
                           CS_BYTEALIGNWINDOW
    m2m wc.lpfnWndProc,    lpWndProc
    mov wc.cbClsExtra,     NULL
    mov wc.cbWndExtra,     NULL
    m2m wc.hInstance,      hInstance
    m2m wc.hbrBackground,  bColor
    mov wc.lpszMenuName,   NULL
    m2m wc.lpszClassName,  lpClassName
    m2m wc.hIcon,          Icon
    m2m wc.hCursor,        Cursor
    m2m wc.hIconSm,        Icon

    invoke RegisterClassEx, ADDR wc

    ret


Further more, if I use LOCAL var:DWORD in WndProc it does not work! Why?
Example is Icz's tute 25. Simple bitmap:
WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
   LOCAL ps:PAINTSTRUCT
   LOCAL hdc:HDC
   LOCAL hMemDC:HDC
   LOCAL rect:RECT
   LOCAL hBitmap:HDC   <---it does not show picture if hBitmap is defined in WndProc! Works fine if hBitmap is defined in .DATA?

   .if uMsg==WM_CREATE
      invoke LoadBitmap,hInstance,IDB_MAIN
      mov hBitmap,eax
Why? Why pc and hdc and rect work nicely but hbitmap does not?


Another question:
OPTION PROC PUBLIC is default in MASM. And that means that I can make jmp IN some fn if I want. Even if it's not smart option. Right? And that also means that I can not use same label names for jmps and can not use same var names in different fn. Am I right?
But if OPTION PROC PRIVATE is set every fn is threated as closed set of instructions. Somewhat like C/C++ right?
Is PRIVATE little better for bigger project?





hutch--

RegisterClassEx() works the same way as it does with any other language. You place the appropriate values in the WNDCLASSEX structure then pass the address of the WNDCLASSEX structure to the RegisterClassEx() function. It is a common technique used in Windows coding that came from the VAX guys who designed the architecture of 32 bit Windows.

MASM handles scope issues like any other language, LOCAL variables are built on the stack and are only available for the duration of the procedure they are created within where GLOBAL variables are stored in the DATA section and can be accessed from anywhere in the module and can be modified by one proc for use in another.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

Timbo

Greetings,

To further what Hutch said, you will find that is perfectly safe and encouraged to pass the address of a structure that is residing on the stack to win32 apis.  This is because the api generally copies the structure before returning (and the timely demise of your temporary data). If you don't need access to the structure data later on, just let it live (and die) on the stack.

To answer your question regarding the bitmap, I think you will find that the first time through the WNDPROC your bitmap handle is valid (during your WM_CREATE handler) and then it is promptly erased when your WNDPROC returns.  This explains why it works as a global and doesn't as a local.

Regards,
Tim

CoR

:cheekygreen: All right! IT WORKS!  :green Yeeeee, thanks a lot  :toothy

You know, sometimes it's tough to be beginner.

Timbo, you are right!
If bmp is loaded in WM_CREATE it's deleted later when WndProc exit the stack. But if I load bmp in WM_PAINT it works  :bg
Basically, I realize for the first time, that all fn are constantly being called depending on WM_. CPU really has tough job in WndProc.

Last two questions:

1. If I want to make more than one window with same wc class and same WndProc, should I try to put as many as possible variables on the stack?

2. Should I use ret after every WM_?
@@:
cmp uMsg,WM_something
jne @F
...code...
ret

@@:
cmp uMsg,WM_something else
jne @F
...code...
ret

@@:
Just to speed up little bit exiting from WndProc. Or I have to check all possible WM_ combinations I need before exiting WndProc? I do not think uMsg can have more than one value... But better to ask and be on safe side :)

ic2

QuoteI do not think uMsg can have more than one value...

; WM_MOUSEMOVE || uMsg == WM_NCMOUSEMOVE.....

    .ELSEIF uMsg == 200h || uMsg == 0A0h



hope you don't mind the butt-in with an extra question ...  uMsg seems to handle these two values (if they are actually separate values) when using the .if statement.  My question is how would do it using cmp and jne  with this line of code ?

Timbo

Cor,

You don't want to load the bitmap with each repaint.  You will be loading the same bitmap over and over again. I would just use a global to store the handle and load it once in WM_CREATE handler.  Don't forgot to delete it when you are done with it with DeleteObject.
See MSDN:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/devcons_1vsk.asp

Also, you should use ret to short circuit the WNDPROC but don't forget to set eax appropriately before doing so.

ic2,

I would use cmp x2 against those values and jne over your handler for these values.  You could also use cmp to je to the same label for both cases (assuming you are handling the messages in the same fashion).  I'm a newcomer to assembly myself, so maybe someone else can chime in with a more optimized approach to this.

Regards,
Tim

ic2

"short circuit"   Heavy ...  Welcome Timbo :)

QuoteYou could also use cmp to je to the same label for both cases

There a lot of things i don't know but I never, I mean, i NEVER  came close to even think of that and i tried HARD ()... Thanks a lot, ic now.  but what do you mean by the cmp x2 idea.  Could you draw an example.  It even sounds like FUN ...

CoR

Ok, now I am puzzled. As ic2 have said uMsg seemingly can have more that one value! Welcome to quantum computer world  :green2
I think I have seen one example where we check uMsg for WM_keystroke and WM_mouse click. How is that possible?

Quote from: Timbo on March 08, 2007, 12:26:00 AM
Also, you should use ret to short circuit the WNDPROC but don't forget to set eax appropriately before doing so.
"Short circut" WndProc... And that's good, right? I mean if it's done right. I saw come examples where ret is used after every WM_ case. But I also saw many examples there WM_ checking just propagate till WM_DESTROY where we ret 0 in eax.
What's better? Or both are ok to be used?

Quote from: Timbo on March 08, 2007, 12:26:00 AM
You don't want to load the bitmap with each repaint.  You will be loading the same bitmap over and over again.
I realize that. But if I want to make multiwindow it's either constant reloading of bmp or one global load of every bmp I'll use... Well, maybe you are right. One global load for all WndProc will be better  :toothy

hutch--

CoR,

The second parameter passed to the WndProc can be any of a large number of different Windows messages, this is normal operation for a message driven windowing system like Windows. For any active window running in the system, it has a reasonably large number of messages sent to it on a regular basis ranging from user triggered events (WM_COMMAND etc ...) to automatic repaints if a window has been covered by another window and of course many others that perform specific functions.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

ic2

Here is an example i use from masm32/icztutes/Tute20/subclass.asm with triple plus values in it that makes it a bit more interesting.

Since you do things the right way by wanting to know why things work this may prove strongly related to what hutch just said.  Now I'm looking into why things happen more seriously myself instead of just finding ways to step around it.  So it's all about the larger, whatever goes forward and can come next ... 1234 — ABCD

.if uMsg== 102h       ;  WM_CHAR
mov eax,wParam
.if (al>="0" && al<="9") || (al>="A" && al<="Z") || (al>="a" && al<="z") || al==8h
.if al>="a" && al<="z"
sub al,20h

.endif


Here is a sample of the things i would do if something simply can't be solved.  So i step around it.  Stuff like this is what makes ASM so exciting to me :) ...  In the start up proc I jump over or run things once by using a cmp something

; WM_PAINT ............................................

    .if uMsg == 0Fh ; WM_PAINT

cmp flag_1, 1
jz  NOT_ON_START_UP

    CALL PAINT_BAR

NOT_ON_START_UP:

.endif


;....................................................
Down in the control proc I turn it on or off.  Maybe something like this will help when demanding one time per anything.

.elseif wmsg == 201h ; WM_LBUTTONDOWN
mov eax, hWin
.if eax == [ Butt_1 ]

;  Turn PAINT_BAR on....  Now After_START_UP
   mov flag_1, 0


These are only things you may already know but sometimes even i need a reminder when i get stuck on a problem and refuse to give up until i find a new or better way.

CoR

Thanks for replies!

But I still would love to know what is proper way of using ret in WndProc.
Should I use it in every WM_ check or to let checking propagate till WM_DESTROY?

CoR

I have been reading Charles Petzold - Programming Windows 5th ed.chm and he exits WM_check with return 0.
So it looks safe to:
xor eax,eax
ret
after every WM_message.

hutch--

CoR,

The correct value as a return in a WndProc depends on the message being processed. With most messages you allow the default processing using the return value from "DefWindowProc()". When a message specifies returning ZERO you then zero EAX and call ret (RETN) so that the default processing is not performed.

Now note that this is NOT the case with a dialog message handling procedure (DlgProc) where you normally set EAX to ZERO then call ret (RETN).
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

CoR

Mmmmmm, thanks Hutch. I am not blind any more  :red

Win32hlp for EVERY WM_message clearly states:
QuoteReturn Values
-If an application processes this message, it should return zero.
-The return value indicates the... It may be any of the following: ...
- so on...


The eye do not see what mind do not understand!


Shantanu Gadgil

Hi CoR,
you need not ret from each message...
Another method is have only a ret at the end on the proc
.if eax == WM_xxx
.elseif eax == WM_yyy
.else
    invoke CallWindowProc(....) / (or FALSE for DlgProc)
    ret
.endif

xor eax, eax
ret


Single point of exit is what I am more comfortable with!  :bg

HTH,
Shantanu
To ret is human, to jmp divine!