News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

StringCbPrintfA

Started by guga, August 03, 2014, 10:29:00 PM

Previous topic - Next topic

guga


Proc StringCbPrintfA:
    Arguments @pszDest, @cbDest, @pszFormat
    Local @cchDest, @vaList
    Uses ebx, esi, edi

    mov eax D@cbDest | mov D@cchDest eax
    call StringValidateDestA D@pszDest, D@cchDest, &NULL, &STRSAFE_MAX_CCH
    If eax = &SUCCEEDED
        ; there is no need to make vaList global. Since it is only a placeholder of the parameters beyond pszFormat
        lea eax D@pszFormat | add eax 4 | mov D@vaList eax
        call StringVPrintfWorkerA D@pszDest, D@cchDest, &NULL, D@pszFormat, D@vaList
        mov D@vaList 0
    End_If
   ; EndSTD macro is just the end of a procedure on a stdcall form. i.e: only a ret value (it is not an "ret N")
EndSTD


Proc StringValidateDestA:
    Arguments @pszDest, @cchDest, @pcchDestLength, @cchMax
    Local @hr
    Uses ebx, edi

    mov D@hr &S_OK
    mov edi D@pcchDestLength
    mov ebx D@cchMax
    .If_Or D@cchDest = 0, D@cchDest > ebx
        mov D@hr &STRSAFE_E_INVALID_PARAMETER
    .End_If

    .If edi <> 0
        If D@hr = &S_OK
            call StringLengthWorkerA D@pszDest, D@cchDest, edi
            mov D@hr eax
        Else
            mov D$edi 0
        End_If
    .End_If

    mov eax D@hr

EndP

Proc StringLengthWorkerA:
    Arguments @psz, @cchMax, @pcchLength
    Local @hr
    Uses ebx, edi, ecx

    mov D@hr &S_OK

    mov edi D@psz
    mov ebx D@cchMax
    While ebx <> 0
        mov eax edi
        movzx ecx B$eax
        On ecx = 0, jmp L1>
        inc edi
        dec ebx
    End_While
L1:
   

    ; the string is longer than cchMax
    .If ebx = 0
        mov D@hr &STRSAFE_E_INVALID_PARAMETER
    .End_If

    mov edi D@pcchLength
    .If edi <> 0
        If D@hr = &S_OK
            mov eax D@cchMax | sub eax ebx | mov D$edi eax
        ELse
            mov D$edi 0
        End_If
    .End_If

    mov eax D@hr

EndP

Proc StringVPrintfWorkerA:
    Arguments @pszDest, @cchDest, @pcchNewDestLength, @pszFormat, @argList
    Local @hr, @ccchMax
    Uses ebx, esi, edi

    mov D@hr &S_OK
    ; leave the last space for the null terminator
    mov eax D@cchDest | dec eax | mov D@ccchMax eax

    C_call 'msvcrt._vsnprintf' D@pszDest, D@ccchMax, D@pszFormat, D@argList

    .If_Or eax <s 0, eax => D@ccchMax

        If eax <> D@ccchMax
            mov D@hr &STRSAFE_E_INSUFFICIENT_BUFFER
        End_If

        ; need to null terminate the string, and truncate it
        mov eax D@pszDest | add eax D@ccchMax | mov B$eax 0
        mov eax D@ccchMax

    .End_If

    If D@pcchNewDestLength <> 0
        mov ebx D@pcchNewDestLength
        mov D$ebx eax
    End_If
    mov eax D@hr

EndP



Example of usage:

[pszDest: B$ 0 #MAX_SIZE]
[MAX_SIZE 30]

C_call StringCbPrintfA pszDest, MAX_SIZE, {B$ "%s %d + %d = %d.", 0}, {B$ "The answer is", 0}, 1, 2, 3


Note:
C_call macro is only a call instruction followed by the stack adjustments "add esp (x*4)", where X is the number of arguments passed through the function
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

guga

One question...

I´m a bit unconfortable with this M$ safestring function, since it does not return the length of the passed string. I wonder if it wouldn´t be better to use SetLastError to handle the HRESULT error cases, and make the function returns the lenght of the string if sucess or FALSE/NULL if the function fails. Then, in cases of failure the user should use the function GetLastError to retrieve it. Since the error is n a HRESULT form, then simply uses functions like ReportWinError to retrieve the proper message.

What do you guys think ?

Although StringCbPrintfEx can be used to retrieve the passed size of the string, i think it is a bit unnecessary port instead simply only to make the function return the proper size on sucess or NULL/FALSE on fail.
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com

guga

A replacement to include the output of the len in eax and yet retrieve information about errors can be done as below:


Proc StringCbPrintfA:
    Arguments @pszDest, @cbDest, @pszFormat
    Local @cchDest, @iret, @vaList
    Uses ebx, esi, edi

    mov D@iret 0
    mov eax D@cbDest | mov D@cchDest eax
    call StringValidateDestA D@pszDest, D@cchDest, &NULL, &STRSAFE_MAX_CCH
    .If eax = &SUCCEEDED
        ; there is no need to make vaList global. Since it is only a placeholder of the parameters beyond pszFormat
        lea eax D@pszFormat | add eax 4 | mov D@vaList eax
        lea ebx D@iret
        call StringVPrintfWorkerA D@pszDest, D@cchDest, ebx, D@pszFormat, D@vaList
        mov D@vaList 0
        If eax <> &S_OK
            call 'KERNEL32.SetLastError' eax | mov D@iret 0
        End_If
    .Else
        call 'KERNEL32.SetLastError' eax | mov D@iret 0
    .End_If

    mov eax D@iret

EndSTD
Coding in Assembly requires a mix of:
80% of brain, passion, intuition, creativity
10% of programming skills
10% of alcoholic levels in your blood.

My Code Sites:
http://rosasm.freeforums.org
http://winasm.tripod.com