News:

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

Working WndProc with no stack frame.

Started by ThoughtCriminal, January 30, 2006, 09:43:01 AM

Previous topic - Next topic

ThoughtCriminal

I always heard a callback always needs a stack frame.  I have a working window where the WndProc has no stack frame.  Perhaps anther set of eyes will fine something.

Note: I using an unusual style of programming.  No stack frames anywhere.  ebp+fuction offset is used to call functions.   Because the initial system call to the WndProc seem to trash all the regiters, I need to restore ebp to call the APIs in WndProc.

My WndProc:

WndProc proc
mov ebp,offset FUNC0  
.if esp==2   ;Need to fix-- uMsg = esp+12 but '.if esp-12==2' is not allowed

push 0
call dword ptr[ebp]+PostQuitMessage
.else

push [esp+16]
push [esp+16]
push [esp+16]
push [esp+16]

call dword ptr[ebp]+DefWindowProc

ret 10h
.endif
xor eax,eax
ret 10h

Iczelion tut 3:

WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.if uMsg==WM_DESTROY
invoke PostQuitMessage, NULL
.else
invoke DefWindowProc, hWnd, uMsg, wParam, lParam
ret
.endif
xor eax,eax
ret
WndProc endp



Mostly looking for opnions on the safety om my code.

Thanks.

zooba

Frankly, if you're looking for safety, I don't think you'll find it without a stack frame. Any stack corruption has the potential to crash your program in completely random places (ie, miss one pop, suddenly you've ret'd to the middle of nowhere).

By the way, you can use:

.if DWORD PTR [esp-12] == 2
or
.if esp == (2+12)
depending on exactly what you're trying to test (ie. the value of esp or the value stored on the stack) :U

Tedd

The stack-frame is still created for you by MASM when you use PROC - whether you use it or not :bdg

Before the function, add the following lines to remove the stack-frame
OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE


And after it, to turn it back on again (for other functions, if you want to?)
OPTION PROLOGUE:PrologueDef
OPTION EPILOGUE:EpilogueDef



On other news, I don't see why a callback should/shouldn't require a stack-frame. It's simply a function - called from elsewhere, some number of times, but still simply a function. It doesn't get paused halfway through, there's nothing special about it, it can't preserve any local variables, so the use of a stack-frame is irrelevant.
Any 'state' you want to maintain must be done with global variables in any case - with or without a stackframe :P
No snowflake in an avalanche feels responsible.

lingo

We can use the simpler form too... :lol
MsgProc:
       
    mov eax, [esp+2*4]

    .if eax == WM_CLOSE
jmp     OnClose

    .elseif eax == WM_KEYDOWN
call OnKeydown

    .elseif eax == WM_DESTROY
           jmp ExitProcess
       
    .elseif eax == WM_PAINT
            call OnPaint

    .elseif eax == WM_SIZE
call     OnSize

    .endif
   
jmp DefWindowProc


Regards,
Lingo

ThoughtCriminal

I turned the stack frames off Tedd.  However it is always good to show people how to do it.

zooba-

I will most likely put it in, just did it to see if I could.  Thanks for the example, I dont usually use the high level directives.

lingo-

Thanks for the code.

Ratch

ThoughtCriminal,

Quote
I always heard a callback always needs a stack frame.

     Someone told you wrong.

Quote
Note: I using an unusual style of programming.  No stack frames anywhere.

     I never use stackframes or PROC's.  For stack frames, you have to sacrifice the EBP register in a already register starved CPU.  If you are usinging it to reference variables, you cannot use it for computations without going through a save/restore cycle each time you have need for an extra register.

     As for PROC's, they are an extra software layer that gets between the programmer and the problem.  Do a search on this thread and not how many problems occur, and bandwidth is expended about this directive.   I use the STRUCT directive to keep track of what is on the stack during function calls.  This allows me complete flexibility when calling functions.  For instance, I can PUSH variables many instructions before the function is called.  One would do that if the parameter value is available at the time, and would get lost by the time when the function is called.  This eliminates having to save the parameter value somewhere.  Of course I have to compensate for each PUSH, but after while it becomes second nature to do so.

     As for the WinProc, I search a table of message numbers and correlate it with a jump table to the message handler.  This routine also saves the "sacred four" Windows registers for restoration later, so that these registers are immediately available for the message handlers to use.

     I have published examples of the above methods in  past posts.  If you cannot find them, and are really interested, let me know.  Otherwise, forget what I said.  Ratch

OS

Right I don't used Stack frames either but for those of you who want to try this remember ....
(GENERAL INFO slightly off the WNDPROC topic but important for people who just want to use calls without frames)
Since when you call a function this happens
CALL XXXX =PUSH $ ->JMP XXXX
so @ [ESP] is the return address and any pushs you made are now at [ESP+4]
When the frame is created you (PUSH EBP) (store the register value)
AND then clone the stack pointer (MOV EBP,ESP) NOW When you need to preserve
registers without a stackframe , you have to push ,and that will move your data .

_simplefunc: ;(vara,varb)
PUSH EDX,ECX
MOV EDX,[ESP+12] ;ESP+4(+2 pushs)VARA
MOV ECX,[ESP+16] ;ESP+8(+2 pushs)VARB
POP ECX,EDX
....YOU can either RET XX (bytes pushed to return the stack frame)
....ADJUST the stack (ADD ESP,8)and jump to the return address with a register or global varible
....jmp [ESP] and adjust stack as a C convention type call


I don't allways create functions in the above way but in my library there are no frames and all registers
used get restored ,it's just nice to make a call with a varible in ECX and still have it be there when your call
returns.

Ratch

OS,
     I have stackless frames down to a system, because I use the STRUC directive to make a map of what the stack will look like after the function is called, registers saved, and local storage obtained.  Then I reference everything with respect to ESP and compensate for any PUSH'es and POP's within the subroutine.  When it comes time to return, I backtrack using the  STRUCT to restore the stack to what it was before the subroutine call.  No red tape with PROC's for me.   Ratch

hutch--

I find some humour in these discussions.


55                     push    ebp
89E5                   mov     ebp,esp


C9                     leave
C21000                 ret     10h


You can barely notice a stack frame in a tiny intensive procedure. With something as slow as a WndProc superman could not measure the difference.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

ThoughtCriminal

Ratch-- I'm interested how you use STRUCTS to mange the stack.

hutch--

Just did it to see if it could be done.

Ratch

ThoughtCriminal,
     OK, look at the zipped file below.  It is not important to know what the subroutine is doing.  Observe how the STRUCT is defined according to the subroutine.  At the beginning, note how the registers are saved and local storage obtained.  Notice also how the local variables and the PUSH'ed variables are refenced from the stack, and how the stack references needs to be compensated.  At the end, see how the stack is cleaned up.  In summation the STRUCT helps you to reference important data with reference to the ESP register instead of the EBP register.  I always set the EBP register to the useful constant value of zero unless I need it for something else.  No wrestling with PROC's with this method.   Any questions?  Ratch

[attachment deleted by admin]

Ratch

hutch--,

Quote
You can barely notice a stack frame in a tiny intensive procedure. With something as slow as a WndProc superman could not measure the difference.

     We both know of a member who, not that long ago, argued that it makes hardly any difference as far as human preception is concerned, between a program coded with a high level language or assembler.  Especially if the HLL is done with a good optimizing compilier.  I guess it all boils down to the goals of reaching the fastest speed, shortest code and being a complete control freak.  Ratch

Petroizki

I haven't used stack frames in a long time. I created my own function macros (pmacros), which easily give me the ability to make procedures, which free up the ebp-register for more important things. I see no reason to use ebp-register to refer parameters or locals, if i can in most cases, just as easily do it through esp.

This is a procedure with pmacros, which uses esp-register to refer parameters:
func WndProc, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM
.IF uMsg == WM_DESTROY
fncall PostQuitMessage, NULL
.ELSE
jmp DefWindowProc
.ENDIF

xor eax, eax
ret
endf WndProc

Gustav

> The stack-frame is still created for you by MASM when you use PROC - whether you use it or not

No. It it created only (by MASM) if the proc has parameters or local variables defined. No need to use the prologue/epilogue macros.


EduardoS

using ESP as base register you need 1 byte more for each time you need a local variable or parameters, 5 of them takes more space then the stack frame,
creating a stack frame may be a good ideia when you don't need 7 registers,

Now another question, some routines save the esp in a global variable and use all 8 registers to the proc, what happen if windows interrupt the execution and esp have a random value?