News:

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

varargs and argcount

Started by Matthew, May 18, 2012, 08:58:20 AM

Previous topic - Next topic

Matthew

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

jj2007

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

Matthew

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?

jj2007

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.

Matthew

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

MichaelW

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

eschew obfuscation