News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Wrong result using invoke wspintf

Started by quocsan, March 18, 2025, 10:56:03 PM

Previous topic - Next topic

quocsan

Hi all!

So glad to receive your welcome.
Today, I've fixed the problem when using invoke wsprintf with many parameters (12), by passing params  right-to-left, calling the function and cleaning up the stack by hand.  :badgrin:
I hope to learn more from all of you.

Best regards,

zedd151

Quote from: quocsan on March 18, 2025, 10:56:03 PMToday, I've fixed the problem when using invoke wsprintf with many parameters (12), by passing params  right-to-left, calling the function and cleaning up the stack by hand.  :badgrin:
Maybe you could teach us then.  :tongue: just kidding.
:biggrin:

Happy coding!  :thumbsup:
¯\_(ツ)_/¯   :azn:

'As we don't do "requests", show us your code first.'  -  hutch—

jj2007

Quote from: quocsan on March 18, 2025, 10:56:03 PMI've fixed the problem when using invoke wsprintf with many parameters (12)

What was the problem? Normally, an invoke crt_wsprintf, .... should work without any problems :cool:

quocsan

Quote from: jj2007 on March 19, 2025, 05:24:50 AM
Quote from: quocsan on March 18, 2025, 10:56:03 PMI've fixed the problem when using invoke wsprintf with many parameters (12)
QuoteWhat was the problem? Normally, an invoke crt_wsprintf, .... should work without any problems :cool:

When I used invoke, I passed
edx
among other params. But invoke really seemed to push edx with value different from it was. So I had to make the call by hand.

xor edx, edx
mov eax, edx ; eax = edx = 0
mov dl, Header.m_UpdatedYY ; year of updated date
.if (dl < 80)
add dx, 2000
.else
add dx, 1900
.endif
mov ax, Header.m_RecordSize
push eax
mov ax, Header.m_HeaderSize
push eax
push Header.m_RecordCount
push edx
xor eax, eax
mov al, Header.m_UpdatedMM
push eax
mov al, Header.m_UpdatedDD
push eax
push DbfDesc
mov al, Header.m_ID
push eax
push FileSize
push offset fname
push offset szFmt
push offset szBuffer
call wsprintf
add esp, 4*12


jj2007

Quote from: quocsan on March 19, 2025, 08:26:32 AMinvoke really seemed to push edx with value different from it was

Interesting: this should not happen. The only scenario I could imagine is that one of your arguments is a macro that changes edx. Could you please post a complete example where that problem happens? I'd like to see it under a debugger.

NoCforMe

I second that emotion.
INVOKE does nothing to change any registers, except for EAX if one of the parameters happens to be a memory reference using ADDR, in which case you'll get this for that parameter:
     LEA    EAX, <parameter>
     PUSH   EAX
in which case you cannot use EAX anywhere to the right of that parameter (in the parameter list you give INVOKE). But it'll never mess with EDX, so you must have done something wrong.
Assembly language programming should be fun. That's why I do it.

quocsan

Quote from: jj2007 on March 19, 2025, 08:29:27 AM
Quote from: quocsan on March 19, 2025, 08:26:32 AMinvoke really seemed to push edx with value different from it was

Interesting: this should not happen. The only scenario I could imagine is that one of your arguments is a macro that changes edx. Could you please post a complete example where that problem happens? I'd like to see it under a debugger.

Thank you for your kindness. I shall take your suggestions and shall study the matter deeply when having time.

Well, this is really a utility I write to fix corrupted DBF files caused by sudden electricity cut. To test it, we have to use a company's private DBF files. :biggrin:
Quote from: NoCforMe on March 19, 2025, 08:38:00 AMI second that emotion.
INVOKE does nothing to change any registers, except for EAX if one of the parameters happens to be a memory reference using ADDR, in which case you'll get this for that parameter:
     LEA    EAX, <parameter>
     PUSH   EAX
in which case you cannot use EAX anywhere to the right of that parameter (in the parameter list you give INVOKE). But it'll never mess with EDX, so you must have done something wrong.
Thank you for your notes. I shall pay more attention when using params with invoke.

jj2007

Quote from: quocsan on March 19, 2025, 08:51:39 AMI shall pay more attention when using params with invoke

The interesting point is that you don't have to pay attention. The assembler throws an error if you trash eax with an "addr". The same should happen with edx.

So the question here is "what's wrong?". This is a serious question, and we really need to see the source code where this happens. It could be a bug of the assembler. Therefore I'd really appreciate if you post a short example where invoke fails :thup:

quocsan

Quote from: jj2007 on March 19, 2025, 08:56:57 AM
Quote from: quocsan on March 19, 2025, 08:51:39 AMI shall pay more attention when using params with invoke

The interesting point is that you don't have to pay attention. The assembler throws an error if you trash eax with an "addr". The same should happen with edx.

So the question here is "what's wrong?". This is a serious question, and we really need to see the source code where this happens. It could be a bug of the assembler. Therefore I'd really appreciate if you post a short example where invoke fails :thup:
I shall get back the erroneous version.
Now I think the error was not as I thought.

jj2007

Quote from: quocsan on March 19, 2025, 09:06:56 AMI shall get back the erroneous version.
Thanks, that's really interesting :thup:

NoCforMe

Quote from: jj2007 on March 19, 2025, 08:56:57 AM
Quote from: quocsan on March 19, 2025, 08:51:39 AMI shall pay more attention when using params with invoke

The interesting point is that you don't have to pay attention. The assembler throws an error if you trash eax with an "addr". The same should happen with edx.

How would EDX ever get involved with INVOKE? No interaction there that I know of.
EDX is what I usually use instead of EAX whenever there's an ADDR in the mix.
Assembly language programming should be fun. That's why I do it.

quocsan

Hi all!
Below is the old version of my source code. You can find wsprint easily.
I hope you will show me the light.

.386
.model flat, stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
include \masm32\include\comdlg32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\comdlg32.lib

WinMain proto CALLBACK :DWORD, :DWORD, :DWORD, :DWORD
SelectFile proto
DbfID proto :Byte
ProcessDBF proto

DbfMinFileSize EQU 328 ; 0x148, Estimated value.

.data
DbfHeader struct
    m_ID            DB ?
    m_UpdatedYY     DB ? ; YYMMDD
    m_UpdatedMM     DB ?
    m_UpdatedDD     DB ?
    m_RecordCount   DD ?
    m_HeaderSize    DW ?
    m_RecordSize    DW ?
    m_Reserved      DB 16 DUP(?)
    m_MDXflag       DB ?
    m_CodePage      DB ?
    m_Reserved2     DB 2 DUP(?)
DbfHeader EndS

Dbf_IDx struct
    Desc    DD ?
    ID        DB ?
Dbf_IDx EndS

    _TITLE        DB "DBF checker", 0
    _EOPENFILE    DB "Error opening file.", 0
    _ESEEKFILE    DB "Error moving file pointer.", 0
    _EREADFILE    DB "Error reading file.", 0
    _EWRITEFILE    DB "Error updating file.", 0
    _EFMT_DBF    DB "Invalid header format for DBF file.", 0
    _VALID_DBF    DB "The file structure seems to be OK.", 0
    _OFN_TITLE    DB "Choose the DBF file to check/fix", 0
    szFilter    DB "DBF files (*.DBF)", 0, "*.DBF", 0, 0
    szFsizeFmt    DB "Error %lu:  GetFileSize failed.", 0Ah, 0
    szMsg        DD OFFSET _VALID_DBF
    szFmt        DB "File name: [%s]", 0Ah
                    DB "File size: %lu (bytes)", 0Ah
;                    DB "ID: 0x%02X [%s]", 0Ah
                    DB "ID: 0x%02X", 0Ah
                    DB "Last updated: %02d/%02d/%04d", 0Ah
                    DB "Number of records: %d", 0Ah
                    DB "Header size: %d (bytes)", 0Ah
                    DB "Record size: %d (bytes).", 0Ah, 0Ah
                    DB "Successfully fixed the file structure.", 0
    ; DBF's IDs
    _x02 DB "FoxBASE 1.0", 0
    _x03 DB "FoxBASE 2.x/ Dbase III plus, no memo", 0
    _x04 DB "dBASE IV, no memo", 0
    _x05 DB "dBASE V, no memo", 0
    _x07 DB "Visual Objects for the dBASE III files, no memo", 0
    _x30 DB "Visual FoxPro", 0
    _x31 DB "Visual FoxPro, with auto increment", 0
    _x32 DB "Visual FoxPro with Varchar/ Varbinary", 0
    _x43 DB "dBASE IV SQL table files, no memo", 0
    _x7B DB "dBASE IV with memo", 0
    _x63 DB "dBASE IV SQL system files, no memo", 0
    _x83 DB "FoxBASE 2.x/ dBASE III PLUS, with memo", 0
    _x87 DB "Visual Objects for the dBASE III files, with memo", 0
    _x8B DB "dBASE IV with memo", 0
    _x8E DB "dBASE IV with SQL table", 0
    _xCB DB "dBASE IV SQL table files, with memo", 0
    _xF5 DB "FoxPro 2.x with memo", 0
    _xFB DB "FoxBASE/ FoxPro 2", 0
aID LABEL BYTE ; Array of IDs
    Dbf_IDx <OFFSET _x02, 002h>
    Dbf_IDx <OFFSET _x03, 003h>
    Dbf_IDx <OFFSET _x04, 004h>
    Dbf_IDx <OFFSET _x05, 005h>
    Dbf_IDx <OFFSET _x07, 007h>
    Dbf_IDx <OFFSET _x30, 030h>
    Dbf_IDx <OFFSET _x31, 031h>
    Dbf_IDx <OFFSET _x32, 032h>
    Dbf_IDx <OFFSET _x43, 043h>
    Dbf_IDx <OFFSET _x7B, 07Bh>
    Dbf_IDx <OFFSET _x63, 063h>
    Dbf_IDx <OFFSET _x83, 083h>
    Dbf_IDx <OFFSET _x87, 087h>
    Dbf_IDx <OFFSET _x8B, 08Bh>
    Dbf_IDx <OFFSET _x8E, 08Eh>
    Dbf_IDx <OFFSET _xCB, 0CBh>
    Dbf_IDx <OFFSET _xF5, 0F5h>
    Dbf_IDx <OFFSET _xFB, 0FBh>
aIdSize EQU ($ - OFFSET aID) / sizeof Dbf_IDx

.data?
ZeroMark LABEL BYTE
    hInstance    HINSTANCE ?
    CommandLine    LPSTR ?
    fname        DB MAX_PATH DUP(?)
    szDir        DB MAX_PATH DUP(?)
    ofn            OPENFILENAME <?>
    szBuffer    DB 512 DUP(?)
    Header        DbfHeader <?>
    DbfDesc        DD ?
    hFile        DD ?
    lBytesIO DD ?
    FileSize    DD ?
    fSuccess    DD ?
    RecCount    DD ?
ZeroFillSize EQU $ - OFFSET ZeroMark

.code
start: ; Main entry point.
    invoke RtlZeroMemory, addr ZeroMark, ZeroFillSize ; Zero-fill variable.
    invoke GetModuleHandle, NULL
    mov hInstance, eax
    invoke GetCommandLine
    mov CommandLine, eax
    invoke WinMain, hInstance, NULL, CommandLine, SW_SHOWDEFAULT
    invoke ExitProcess, eax

WinMain Proc Inst:HINSTANCE, hPrevInst:HINSTANCE, CmdLine:LPSTR, CmdShow:DWORD
    invoke SelectFile
    .IF eax == TRUE
        invoke ProcessDBF
    .ENDIF
    ret
WinMain EndP
;
; Returns Desc if ID is valid, NULL otherwise.
DbfID proc uses edi ID:Byte
    xor eax, eax
    push eax
    pop ecx ; ecx=eax=0
    mov dl, ID
    mov edi, OFFSET aID
    sub edi, sizeof Dbf_IDx
    assume edi: ptr Dbf_IDx
ID_check:
    add edi, sizeof Dbf_IDx
    cmp [edi].ID, dl
    jne ID_next
    mov eax, [edi].Desc
    jmp ID_done
ID_next:
    inc ecx
    cmp ecx, aIdSize
    jb ID_check
ID_done:
    assume edi: nothing
    ret
DbfID EndP

SelectFile Proc
    invoke GetCurrentDirectory, MAX_PATH, addr szDir

    push OFFSET szDir
    pop ofn.lpstrInitialDir

    mov ofn.lStructSize, sizeof OPENFILENAME

    mov ofn.hwndOwner, NULL

    push OFFSET szFilter
    pop ofn.lpstrFilter

    mov ofn.nFilterIndex, 1

    push OFFSET fname
    pop ofn.lpstrFile

    push OFFSET _OFN_TITLE
    pop ofn.lpstrTitle

    mov ofn.nMaxFile, MAX_PATH
    mov ofn.Flags, OFN_PATHMUSTEXIST or OFN_FILEMUSTEXIST or OFN_FORCESHOWHIDDEN or OFN_EXPLORER
    invoke GetOpenFileName, addr ofn
    ret
SelectFile EndP

ProcessDBF Proc
    mov hFile, INVALID_HANDLE_VALUE
    invoke SetFileAttributes, addr fname, FILE_ATTRIBUTE_NORMAL;  Force file attr to be normal.
    invoke CreateFile, addr fname, GENERIC_READ or GENERIC_WRITE, NULL, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL
    mov hFile, eax
    .IF eax == INVALID_HANDLE_VALUE
        mov szMsg, OFFSET _EOPENFILE
        jmp Process_Done
    .ENDIF
    invoke GetFileSize, hFile, NULL
    mov FileSize, eax
    .IF eax  == INVALID_FILE_SIZE
        invoke GetLastError
        invoke wsprintf, addr szBuffer, addr szFsizeFmt, eax
        mov szMsg, OFFSET szBuffer
        jmp Process_Done
    .ENDIF
    invoke ReadFile, hFile, addr Header, sizeof Header, addr lBytesIO, 0
    mov fSuccess, eax
    .if (!eax || lBytesIO != sizeof Header)
        mov szMsg, OFFSET _EREADFILE
        jmp Process_Done
    .endif
    invoke DbfID, Header.m_ID
    mov DbfDesc, eax ; Save ID description
    .if (FileSize < DbfMinFileSize || !eax || !Header.m_RecordSize || !Header.m_HeaderSize)
        mov szMsg, OFFSET _EFMT_DBF
        mov fSuccess, FALSE
        jmp Process_Done
    .endif
    xor edx, edx
    push edx
    pop ecx
    mov eax, FileSize
    mov cx, Header.m_HeaderSize
    sub eax, ecx ; (FileSize - Header.m_HeaderSize)
    mov cx, Header.m_RecordSize
    div ecx
    mov RecCount, eax ; = (FileSize - Header.m_HeaderSize) / Header.m_RecordSize
    .if (eax != Header.m_RecordCount) ; Different in record counts!
        invoke SetFilePointer, hFile, 0, 0, FILE_BEGIN
        .if (eax == INVALID_SET_FILE_POINTER)
            mov szMsg, OFFSET _ESEEKFILE
            mov fSuccess, FALSE
            jmp Process_Done
        .endif
        push RecCount
        pop Header.m_RecordCount ; Update correct number of records ...
        ; ... and write back to file at the appropriate file offset
        mov szMsg, OFFSET _EWRITEFILE ; assume error writing
        invoke WriteFile, hFile, addr Header, sizeof Header, addr lBytesIO, NULL
        mov fSuccess, eax
        .if (eax != 0 && lBytesIO == sizeof Header)
            invoke FlushFileBuffers, hFile
            xor edx, edx
            mov dl, Header.m_UpdatedYY ; year of updated date
            .if (dl < 80)
                add dx, 2000
            .else
                add dx, 1900
            .endif
            invoke wsprintf, \
                    addr szBuffer, \
                    addr szFmt, \
                    addr fname, \
                    FileSize, \
                    Header.m_ID, \    ;DbfDesc, \
                    Header.m_UpdatedDD, Header.m_UpdatedMM, edx, \
                    [Header.m_RecordCount], \
                    [Header.m_HeaderSize], \
                    [Header.m_RecordSize]
            mov szMsg, OFFSET szBuffer ; to show detailed information.
        .endif
    .endif
Process_Done:
    .IF hFile != INVALID_HANDLE_VALUE
        invoke CloseHandle, hFile
    .ENDIF
    .IF fSuccess != 0
        mov eax, MB_ICONINFORMATION
    .ELSE
        mov eax, MB_ICONERROR
    .ENDIF
    invoke MessageBox, NULL, szMsg, addr _TITLE, eax
    mov eax, fSuccess
    ret
ProcessDBF EndP

end start

NoCforMe

In your code you have
        invoke wsprintf, addr szBuffer, addr szFsizeFmt, eax

        invoke wsprintf, \
                    addr szBuffer, \
                    addr szFmt, \
                    addr fname, \
                    FileSize, \
                    Header.m_ID, \    ;DbfDesc, \
                    Header.m_UpdatedDD, Header.m_UpdatedMM, edx, \
                    [Header.m_RecordCount], \
                    [Header.m_HeaderSize], \
                    [Header.m_RecordSize]
Both of those look fine to me, although I haven't checked the call to wsprintf() against the format string. You seem to understand that the parameter list must match the sequence of format specifiers (%02X, %02d, etc.).

So what's the problem you want us to help you with?

One thing I'm not sure of is the format specifier %lu; is this supposed to be a DWORD or is a QWORD (8 bytes)? If it's just a DWORD then just use %u, which is a standard integer (32 bits).

And it's OK to use EAX as the parameter here, since it's to the right of the first ADDR in the parameter list. If there's a problem there the assembler will issue a warning that the register will be overwritten.

One thing I'd change is to use carriage-return/line-feed pairs at the ends of lines, instead of just the line feeds that you have:
13, 10instead of just 0Ah (and no reason to use hex here)
Assembly language programming should be fun. That's why I do it.

quocsan

Well, as you noted, I doubt that my problem is from the size of parameters.
I shall check this matter when I have time.
Thank you so much for your help, NoCforMe!

NoCforMe

Assembly language programming should be fun. That's why I do it.