The MASM Forum Archive 2004 to 2012

General Forums => The Campus => Topic started by: Matthew on May 18, 2012, 08:58:20 AM

Title: varargs and argcount
Post by: Matthew on May 18, 2012, 08:58:20 AM
I am attempting to use vararg but I cant seem to figure out how to use it properly.

I notice there is a macro argcount, but it always returns 1 regardless of how many params i passed.


.386
.model flat, stdcall
option casemap:none ;case sensitive

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\macros\macros.asm

includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
.data?
.code

varargtest proc C arg1:dword, args:VARARG
mov eax, argcount(args)  ; argcount returns 1, even though there is 5!?

ret
varargtest endp

main proc public
invoke varargtest, 99, 1,2,3,4,5

invoke ExitProcess, 0
ret
main endp

end
Title: Re: varargs and argcount
Post by: jj2007 on May 18, 2012, 09:16:09 AM
argcount counts macro arguments. What you need is arg1, see below

include \masm32\include\masm32rt.inc

    argcount MACRO args:VARARG
      LOCAL cnt
      cnt = 0
      FOR item, <args>
        cnt = cnt + 1
      ENDM
      EXITM %cnt                ;; return as a number
    ENDM

.code
varargtest proc C arg1:dword, args:VARARG
; mov eax, argcount(args)  ; argcount returns 1, even though there is 5!?
mov eax, arg1

ret
varargtest endp

main proc public
invoke varargtest, 99, 1,2,3,4,5
MsgBox 0, str$(eax), "Hi", MB_OK
invoke ExitProcess, 0
ret
main endp

end main
Title: Re: varargs and argcount
Post by: Matthew on May 18, 2012, 09:31:23 AM
Your example will return 99 everytime, regardless of how many parameters are passed in the varargtest proc.

Are you saying I need to explicitly inform the proc how many params I am passing?
Title: Re: varargs and argcount
Post by: jj2007 on May 18, 2012, 09:51:13 AM
Quote from: Matthew on May 18, 2012, 09:31:23 AM
Are you saying I need to explicitly inform the proc how many params I am passing?

Yep, that's it... and that's where the macro steps in: You can use argcount in a macro that pushes your params "by hand" and passed the count as last para.
Title: Re: varargs and argcount
Post by: Matthew on May 18, 2012, 01:56:30 PM
Because basically I was writing some logging functionality which uses crt_sprintf.

I was hoping I could just wrap crt_sprintf format and vararg parameters, whilst giving an opportunity to modify the output of crt_sprintf before i used it (e.g append a timestamp).

after your response regarding the macros, I came up with this which seems to work, but I am always thinking could I have done it better...

I was also wondering in the macros in \masm32\macros\macros.asm I see some mangled variable names in the macros, so i mangled my logbuffer variable, is this common practice?


WriteLogMessage proc szMessage:LPSTR, bWriteTimeStamp:BYTE
local dwBytesWritten:DWORD
local dwMessageLength:DWORD
local systemTime:SYSTEMTIME
local dateTimeBuffer[64]:BYTE

.if bWriteTimeStamp == 1
invoke GetLocalTime, addr systemTime
invoke crt_sprintf, addr dateTimeBuffer, chr$("%04d-%02d-%02d %02d:%02d:%02d.%03d : "), systemTime.wYear, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, systemTime.wMilliseconds

invoke StrLen, addr dateTimeBuffer
mov dwMessageLength, eax

invoke WriteFile, hFile, addr dateTimeBuffer, dwMessageLength, addr dwBytesWritten, NULL
.endif

invoke StrLen, szMessage
mov dwMessageLength, eax

invoke WriteFile, hFile, szMessage, dwMessageLength, addr dwBytesWritten, NULL
invoke WriteFile, hFile, chr$(13,10), 2, addr dwBytesWritten, NULL

ret
WriteLogMessage endp

LogMessage macro szFormat,bWriteTimeStamp:req, args:vararg
local l_o_g_b_u_f_f_er
.data?
l_o_g_b_u_f_f_er BYTE 2048 dup(?)
.code

invoke crt_sprintf, addr l_o_g_b_u_f_f_er, chr$(format), args
invoke WriteLogMessage, addr l_o_g_b_u_f_f_er, bWriteTimeStamp
EXITM <>
endm

main proc
LogMessage "%d,%d%d", TRUE, 100, 200, 300

ret
main endp
Title: Re: varargs and argcount
Post by: MichaelW on May 18, 2012, 02:38:08 PM
If your procedure uses the C calling convention, and the variable arguments are of a uniform size, and you don't mind using a hack, then the procedure can determine the number of arguments by examining the instruction at the return address that removes the arguments from the stack. Note that this code is limited to 255 bytes for the arguments = 63 dwords. Tested with ML 6.15.8803 only.

;==============================================================================
include \masm32\include\masm32rt.inc
;==============================================================================
.data
.code
;==============================================================================
testproc proc C args:VARARG
    xor eax, eax                ; assume zero args
    mov edx, [ebp+4]            ; get return address
    cmp WORD PTR[edx], 0C483h   ; opcode for add esp,n
    je  @F
    ret
  @@:
    movzx eax, BYTE PTR[edx+2]  ; get value added to esp
    shr eax, 2                  ; divide by 4 for arg count
    ret
testproc endp
;==============================================================================
start:
;==============================================================================
    invoke testproc
    printf("%d\n",eax)
    invoke testproc,1
    printf("%d\n",eax)
    invoke testproc,1,2
    printf("%d\n",eax)
    invoke testproc,1,2,3
    printf("%d\n",eax)
    invoke testproc,1,2,3,4
    printf("%d\n",eax)
    invoke testproc,1,2,3,4,5
    printf("%d\n",eax)
    invoke testproc,1,2,3,4,5,6
    printf("%d\n\n",eax)
    inkey
    exit
;==============================================================================
end start