The MASM Forum

Projects => Rarely Used Projects => RosAsm => Topic started by: guga on August 03, 2014, 10:33:51 PM

Title: StringCbPrintfW
Post by: guga on August 03, 2014, 10:33:51 PM

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

    mov eax D@cbDest | shr eax 1 | mov D@cchDest eax
    call StringValidateDestW 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 StringVPrintfWorkerW 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 StringValidateDestW:
    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 StringLengthWorkerW D@pszDest, D@cchDest, edi
            mov D@hr eax
        Else
            mov D$edi 0
        End_If
    .End_If

    mov eax D@hr

EndP

Proc StringLengthWorkerW:
    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 W$eax
        On ecx = 0, jmp L1>
        add edi 2
        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 StringVPrintfWorkerW:
    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._vsnwprintf' 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@ccchMax | mov ecx D@pszDest | lea edx D$ecx+eax*2
        add ecx edx | mov W$ecx 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: W$ 0 #MAX_SIZE]
[MAX_SIZE 30]

C_call StringCbPrintfW pszDest, (MAX_SIZE*2), {U$ "%s %d + %d = %d.", 0}, {U$ "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