The MASM Forum

General => The Campus => Topic started by: quocsan on March 18, 2025, 10:56:03 PM

Title: Wrong result using invoke wspintf
Post by: quocsan on March 18, 2025, 10:56:03 PM
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,
Title: Re: Wrong result using invoke wspintf
Post by: zedd151 on March 18, 2025, 11:28:54 PM
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:
Title: Re: Wrong result using invoke wspintf
Post by: 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)

What was the problem? Normally, an invoke crt_wsprintf, .... should work without any problems :cool:
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 08:26:32 AM
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

Title: Re: Wrong result using invoke wspintf
Post by: 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.
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 08:38:00 AM
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.
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 08:51:39 AM
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.
Title: Re: Wrong result using invoke wspintf
Post by: 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:
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 09:06:56 AM
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.
Title: Re: Wrong result using invoke wspintf
Post by: jj2007 on March 19, 2025, 09:08:32 AM
Quote from: quocsan on March 19, 2025, 09:06:56 AMI shall get back the erroneous version.
Thanks, that's really interesting :thup:
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 09:20:43 AM
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.
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 11:23:36 AM
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
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 11:44:43 AM
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)
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 12:03:43 PM
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!
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 12:11:07 PM
So what is your problem, exactly?
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 12:32:54 PM
Quote from: NoCforMe on March 19, 2025, 12:11:07 PMSo what is your problem, exactly?


(https://i.postimg.cc/sB1Pf8gX/result.jpg) (https://postimg.cc/sB1Pf8gX)


As you can see in the attached image: The resulted string after calling wspintf was not as expected. 6 numbers from after "Last updated: " were all wrong.
At that moment, I was in a hurry, so I had to fix the problem by preparing the call to wspintf by hand, instead of using invoke. That's all.
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 01:37:01 PM
I think I found your problem:
Format for printing the date:
DB "Last updated: %02d/%02d/%04d"

Source of the date data:
DbfHeader struct
    m_ID            DB ?
    m_UpdatedYY     DB ? ; YYMMDD
    m_UpdatedMM     DB ?
    m_UpdatedDD     DB ?

Do you see the problem? The first two fields you're trying to use are defined in the structure as bytes (Header.m_UpdatedDD and Header.m_UpdatedMM); however, wsprintf() is using signed DWORD fields (%02d) to format this data, so it's actually using DWORDs for each field instead of just the one byte.

You can't do this kind of conversion (from a byte to a DWORD) with wsprintf(). You need to extract these two fields, convert them to a DWORD and then pass them as parameters (either in registers or in local variables). That should fix your problem.

You can most easily get them into a DWORD thus:
    MOVZX    EAX, Header.m_UpdatedMM          ;Load a byte and zero-extend it to a DWORD.
    MOV      localMM, EAX

then use localMM as the parameter for wsprintf().

(You should do the same thing with m_ID, since it's also a byte in the structure.
Or alternatively you could make the structure fields DWORDs and store them that way, in which case you can directly use them as parameters to pass to wsprintf().)
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 01:45:59 PM
Good job, NoCforMe!

I know the cause now and will avoid the same fault in future.
Thank you so much for your help!
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 01:48:42 PM
Happy to be of assistance.

Just remember to always keep in mind The Size of Things, since assembly language doesn't have all the guard rails that C or C++ does.
Title: Re: Wrong result using invoke wspintf
Post by: sinsi on March 19, 2025, 01:49:40 PM
Which version of ML are you using? Some early versions had trouble passing bytes as arguments.
I built the code with the latest ML (14.42.34438.0) and it seemed to give proper values, except the year was 0001.
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 01:55:49 PM
Microsoft (R) Macro Assembler Version 6.14.8444.

Now I'm editing my source code as NoCforMe guide.
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 01:56:22 PM
Quote from: sinsi on March 19, 2025, 01:49:40 PMWhich version of ML are you using? Some early versions had trouble passing bytes as arguments.
I built the code with the latest ML (14.42.34438.0) and it seemed to give proper values, except the year was 0001.
So how does wsprintf()* handle byte arguments? does it "promote" them automagically?

I thought not, but who knows?

Post some executable code? I'm curious.

* or any other function for that matter
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 02:25:35 PM
The result is perfect!
.if (eax != 0 && lBytesIO == sizeof Header)
invoke FlushFileBuffers, hFile
movzx edx, Header.m_UpdatedYY ; year of updated date
.if (dl < 80)
add dx, 2000
.else
add dx, 1900
.endif
movzx ecx, Header.m_UpdatedMM
movzx ebx, Header.m_UpdatedDD
movzx eax, Header.m_ID
movzx edi, Header.m_HeaderSize
movzx esi, Header.m_RecordSize
invoke wsprintf, \
addr szBuffer, \
addr szFmt, \
addr fname, \
FileSize, \
eax, \
DbfDesc, \
ebx, ecx, edx, \
Header.m_RecordCount, \
edi, \
esi
mov szMsg, OFFSET szBuffer ; to show detailed information.
.endif
Title: Re: Wrong result using invoke wspintf
Post by: sinsi on March 19, 2025, 02:28:20 PM
Comparison
Macro Assembler Version 6.14.8444     Macro Assembler Version 14.42.34438.0
push    0                             push    small 0
push    small [word_40388A]           push    small [word_40388A]
push    0                             push    small 0
push    small [word_403888]           push    small [word_403888]
push    dword_403884                  push    dword_403884
push    edx                           push    edx
push    0                             push    small 0
mov     al, byte_403882               mov     al, byte_403882
movzx   ax, al                        movzx   ax, al
push    ax                            push    ax
push    0                             push    small 0
mov     al, byte_403883               mov     al, byte_403883
movzx   ax, al                        movzx   ax, al
push    ax                            push    ax
push    0                             push    small 0
mov     al, byte_403880               mov     al, byte_403880
movzx   ax, al                        movzx   ax, al
push    ax                            push    ax
push    dword_4038AC                  push    dword_4038AC
push    offset FileName               push    offset FileName
push    offset aFileNameSFileS        push    offset aFileNameSFileS
push    offset stru_403634.pvReserved push    offset stru_403634.pvReserved
call    wsprintfA                     call    wsprintfA
add     esp, 2Ch                      add     esp, 2Ch
Notice push 0 and push small 0?
ML6  pushes a 32-bit 0 then a word -> 48 bits
ML14 pushes a 16-bit 0 then a word -> 32 bits - correct

The problem occurs anywhere that you supply a byte as an argument in 32-bit code.
Another reason to ditch ML6.
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 02:39:46 PM
Quote from: sinsi on March 19, 2025, 02:28:20 PMThe problem occurs anywhere that you supply a byte as an argument in 32-bit code.
Another reason to ditch ML6.
OK;
1. I guess it never occurred to me to even try that (use a byte argument in the first place with the hope that "someone" would "take care of it"). So no reason here not to use the old stuff.

2. Never saw the qualifier "small" before: means byte? word?
Title: Re: Wrong result using invoke wspintf
Post by: NoCforMe on March 19, 2025, 02:42:20 PM
Quote from: quocsan on March 19, 2025, 02:25:35 PMThe result is perfect!
.if (eax != 0 && lBytesIO == sizeof Header)
    invoke FlushFileBuffers, hFile
    movzx edx, Header.m_UpdatedYY ; year of updated date
    .if (dl < 80)
        add dx, 2000
    .else
        add dx, 1900
    .endif
    movzx ecx, Header.m_UpdatedMM
    movzx ebx, Header.m_UpdatedDD
    movzx eax, Header.m_ID
    movzx edi, Header.m_HeaderSize
    movzx esi, Header.m_RecordSize
    invoke wsprintf, \
        addr szBuffer, \
        addr szFmt, \
        addr fname, \
        FileSize, \
        eax, \
        DbfDesc, \
        ebx, ecx, edx, \
        Header.m_RecordCount, \
        edi, \
        esi
    mov szMsg, OFFSET szBuffer ; to show detailed information.
.endif
Good!

Since you're using non-volatile (i.e., "sacred") registers there--EBX, ESI, EDI--be sure to save and restore them around this code so they don't get trashed. (Probably not necessary if you're doing this in your "WinMain" code, or any top-level code in a Win32 program, but should always be done elsewhere.)
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 02:46:20 PM
Quote from: NoCforMe on March 19, 2025, 02:42:20 PMGood!

Since you're using non-volatile (i.e., "sacred") registers there--EBX, ESI, EDI--be sure to save and restore them around this code so they don't get trashed. (Probably not necessary if you're doing this in your "WinMain" code, or any top-level code in a Win32 program, but should always be done elsewhere.)
Yes, I did some changes in the source code, including preserving registers.
ProcessDBF Proc uses ebx edi esi
Title: Re: Wrong result using invoke wspintf
Post by: TimoVJL on March 19, 2025, 05:01:43 PM
    mov RecCount, eax ; = (FileSize - Header.m_HeaderSize) / Header.m_RecordSize
    .if (eax != Header.m_RecordCount) ; Different in record counts!
stupid question, what this "fix" means ?
DBF-file can have also deleted records, so actual file size can be bigger ?

EDIT: 'Number of records in the database file' can also include deleted records.
Most DBF writers just add new record end of file and don't touch deleted records.

EDIT:
Xbase Data file (*.dbf) (https://www.clicketyclick.dk/databases/xbase/format/dbf.html#DBF_STRUCT)

QuoteEnd-of-file
dBASE II regards any End-of-File 1Ah value as the end of the file. dBASE III regard an End-of-File as an ordinary character, however it appends an extra End-of-File character at the physical end of the file.
If the file is packed the physical size of the file may be larger than the logical i.e. there may be garbage after the EOF mark.
Title: Re: Wrong result using invoke wspintf
Post by: quocsan on March 19, 2025, 06:12:34 PM
Quote from: TimoVJL on March 19, 2025, 05:01:43 PM    mov RecCount, eax ; = (FileSize - Header.m_HeaderSize) / Header.m_RecordSize
    .if (eax != Header.m_RecordCount) ; Different in record counts!
stupid question, what this "fix" means ?
DBF-file can have also deleted records, so actual file size can be bigger ?

A DBF file is corrupted when something in its header is wrong. According to my study, deleted records are marked with asterisks ('*') at the beginning of record data, and other details in its header remain as usual.

The fix I've done is: Updating the header with correct number of records.
Title: Re: Wrong result using invoke wspintf
Post by: jj2007 on March 19, 2025, 10:31:29 PM
Compliments, quocsan - you are a new member but apparently not a newbie :thumbsup: