Hi,
well, I did search, but I didn't know how this is exactly called
.. sry then, if there are several topics about that.
What I need is coding a procedure, which can handle an undefined amount of parameters.
You know like for instance WSPRINTF does:
invoke wsprintf,addr szOutput,addr szFormat,11111111h,22222222h,33333333h,... (x times)
Or was this a bad example, coz the amount is handled by the number of '%' in the end :?
Anyway somehow this function also has to correctly deal with the stack, right ?
SomeProc PROTO :DWORD,:DWORD,??????....
SomeProc proc :DWORD,:DWORD,??????....
??????
(my code)
leave (?????)
ret
SomeProc endp
Thank you :)
Only now got the idea to look at the proc definition for wsprintf in the user32.inc.. DOOH :red
You're looking for a longer time, and once you start asking, you find it yourself after some mins :green
'C' was the thingy !
SomeProc PROTO C :VARARG
SomeProc proc c :VARARG
Ok, the stack prob is solved. But what would be the smartest way to check how many args
have been pushed (e.g. need to be handled) ??
'Thanks again' ^^
Quote from: Kernel on March 20, 2008, 10:46:35 PM
But what would be the smartest way to check how many args
have been pushed (e.g. need to be handled) ??
Push a magic 7654321 as last value... or push the number of args as first value.
Kernel,
I have always just passed another argument with the count.
Here is the example from the "MASM Programmer's Guide" converted to 32-bit code, see "Using VARARG" in Chapter 7.
addup3 PROC C numargs:DWORD, args:VARARG
xor eax, eax ; clear work registers
xor edx, edx
.WHILE numargs > 0 ; numargs has number of arguments
add eax, args[edx] ; args has the first argument
dec numargs ; point to next argument
add edx, 4
.ENDW
ret ; result is in eax
addup3 ENDP
@both:
Thanks for the replies :)
Yea passing the numer of args also came to my mind, coz I can't
'Push a magic 7654321 as last value'... due to needing a possible range
from 0 to FFFFFFFF :\
I was again stepping through 'wsprintf' to find out the trick (coz it doesn't use any numArgs),
but got too confused after the sh!tload of code there.
I thought I could calculate it somehow by substracting [ESP] from EBP+8 or something,
but that would have been to easy I guess :\
If I got LOCALS in the procedure, which calls my c-routine, then such a calculation is messed up already...
Here's part of a proc I wrote:
GridMsg proc SYSCALL Public uses esi edi ebx GridHandle:dword,Request:dword,Parms:vararg
maxparm = 6 ; ignore any excess entries greater than this
; locals are saved in reverse order on stack to be in same order as call
local Parm6,Parm5,Parm4,Parm3,Parm2,Parm1
mov eax,[ebp+4] ; get the return address (address after the call in the calling procedure)
; the instruction at the return address will be ADD ESP,nnnn where nnnn is number of bytes
movzx ecx,byte ptr [eax+2] ; which will contain the number of bytes to remove from the stack
shr ecx,2 ; convert to dword count
dec ecx ; skip 2 fixed arguments before the vararg
dec ecx
lea edi,Parm1 ; address of local parms
lea esi,Parms ; address of input
m2m edx,maxparm ; available parm spots
.repeat
xor eax,eax ; default value for parm
jecxz usedefault ; check number of input parms left
lodsd ; pick up next parm
dec ecx
usedefault:
stosd ; save
dec edx
.until Zero?
Works for me, and I don't have to count parms every time I call it.
There's no way to know how many parameters were passed.
wsprintf uses the format-string as an indicator of the count.
That's why, generally you should specify a "numArgs" parameter of the proc.
Quote from: Ultrano on March 21, 2008, 06:39:00 AMwsprintf uses the format-string as an indicator of the count.
I saw that all it does is calling 'wvsprintf', but I missed reading about wvsprintf :red
arglist
... The number, type, and interpretation of the arguments depend on
the corresponding format-control specifications in the lpFmt parameter.
@Jimg:
Thank you ! I will try it :)
One question remains: Is that approach safe enough ?
Coz this function is going to be part of a static library, which I want to release...
Thanks to all
Jimg,
Nice code :U
Kernel,
Jimg's code is safe enough if a standard calling convention is followed (ie. if invoke is used). If the function is called in a non-standard way (example below) then it may not be robust. It is best to make the caller responsible for telling the function how many parameters they are passing; the most common method is passing an integer as the parameter before the vararg parameter.
Cheers,
Zooba :U
(An example of a useful non-standard calling optimisation is as follows:)
push value2
push value1
push value0
push OFFSET szFormat1
push OFFSET szOutput1
; first call with a particular format string
call wsprintf
mov [esp+4], OFFSET szFormat2
mov [esp+4], OFFSET szOutput2
; call again with same arguments but different format string and different output
call wsprintf
add esp, 4*5
Yea it works like a charm for me.
I love that code, coz I gotta deal with up to 20 possible args.
Thanks a bunch :bg
As zooba says, it will break if it isn't called with invoke. For a general purpose library, there's no way to force the user to do so.
The way I get around it is to require the user to use a macro I provide in the general include file-
GridMsg Proto SYSCALL GridHandle:dword,Request:dword,Parameters:vararg
; if an error occurs, ecx will contain the error number, else zero
gmsg macro Handle:req,Parameters:vararg
invoke GridMsg,Handle,Parameters
or ecx,ecx ;; test for error
EndM
a sample of usage to set up a grid of buttons:
gmsg hGridb,SetGridSize,eax,edx ;bcols,brows
jnz DoError
gmsg hGridb,SetColVisible,0,FALSE
gmsg hGridb,SetRowVisible,0,FALSE
gmsg hGridb,SetColWidth,gAll,90
gmsg hGridb,SetRowHeight,gAll,15
gmsg hGridb,SetCell3D,gAll,gAll,TRUE
inv GetSysColor,COLOR_BTNFACE
gmsg hGridb,SetCellBackColor,gAll,gAll,eax
gmsg hGridb,SetCellReadOnly,gAll,gAll,TRUE
gmsg hGridb,SetCellTextAlignment,gAll,gAll,DT_CENTER
gmsg hGridb,SetDrawBorder,FALSE
gmsg hGridb,SetColWidth,1,70
gmsg hGridb,SetColWidth,2,22
gmsg hGridb,SetColWidth,5,80
gmsg hGridb,SetColWidth,6,70
of course you could just make the macro count the params and pass the count.