Here is a small Win64 test program to determine the length of a file:
include \masm32\include64\masm64rt.inc
option casemap: none
.data
align 1
msg0 db "This small program is supposed to determine the length of a file:", 10
db "-----------------------------------------------------------------", 10, 10, 0
msg0A db "File length = ", 0
msg0B db " Bytes", 10, 10, 0
msg1 db "All tasks completed.", 10, 10, 0
TheFile db "TestFile.txt", 0 ; our test file
str_format db "%s", 0 ; string format
uint64_format db "%llu",0 ; 64 bit unsigned integer
align 8
hTheFile dq ? ; file handle
TheFileLen dq ? ; file length
.code
; main
; Purpose: Assembly Language Frame
main proc
push rbp ; preserve callee save CPU registers
push rbx
push rdi
push rsi
push r12
push r13
push r14
push r15
; open the file
invoke vc_printf, addr str_format, addr msg0
invoke CreateFileA, addr TheFile, GENERIC_READ, 0, 0, 0, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING
mov hTheFile, rax ; save file handle
; get file size
invoke GetFileSize, hTheFile, 0
mov TheFileLen, rax ; save file length
; print file size
invoke vc_printf, addr str_format, addr msg0A
invoke vc_printf, addr uint64_format, addr TheFileLen
invoke vc_printf, addr str_format, addr msg0B
; close the file
invoke CloseHandle, hTheFile
invoke vc_printf, addr str_format, addr msg1
waitkey
pop r15 ; restore CPU registers
pop r14
pop r13
pop r12
pop rsi
pop rdi
pop rbx
pop rbp
invoke ExitProcess, 0
main endp
end
It doesn't work as it should. Here's the output:
Quote
This small program is supposed to determine the length of a file:
-----------------------------------------------------------------
File length = 5368717616 Bytes
All tasks completed.
That's the point: the examined file is only 800 bytes in size. So the wrong file length is printed.
What's wrong with that? The archive contains the source, the executable program, the batch file and the test file. It is a small ASCII text file with 20 lines of text. Nothing exciting or disreputable.
Gunther
Check your parameters...
include \Masm32\MasmBasic\Res\JBasic.inc ; ## builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
Init ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
Cls
PrintLine Chr$("This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
PrintLine "This small program is supposed to determine the length of a file:"
hTheFile equ <rbx>
mov hTheFile, rv(CreateFileA, Chr$("GetFileSize.asc"), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
jinvoke GetFileSize, hTheFile, 0
Print Str$("The file size is %i bytes\n", rax)
jinvoke CloseHandle, hTheFile
Inkey "all is fine"
EndOfCode
Output:
This program was assembled with ml64 in 64-bit format.
This small program is supposed to determine the length of a file:
The file size is 4254 bytes
all is fine
Of course, if you are dealing with large files, you need a second argument to get the high dword.
the resulting question is prob ...
is this implying that 1 gig in 4 is allocated for potential file overhead [ sector / track overheads ]????
regards mikeb
uint64_format db "%llu",0 ; 64 bit unsigned integer
this might not be usable with msvcrt.dll,
have to use at least msvcr100.dll ?
Hi Gunther,
Can you try this one ?
invoke vc_printf, addr uint64_format, TheFileLen
Hi Gunther,
A good practice (at least when something went wrong :biggrin: ) is:invoke CreateFileA, addr TheFile, GENERIC_READ, 0, 0, 0, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING
.if eax!=INVALID_HANDLE_VALUE
mov hTheFile, rax ; save file handle
...
.else
; there is an error
.endif
Regards, HSE
HSE .. completely agree .... error checking essential ....
in this case
i doubt whether its failed .. its assumed a max size/ default 32 bit file [ hence the 4gig comment and overheads comment ]
... as create disposition 0 not defined in the SDK
with the open existing as security_batch etc etc
regards mikeb
Quote from: jj2007 on February 10, 2021, 10:46:18 PM
Check your parameters...
Nope, Mike. As mentioned above, the order of arguments needs to be checked, that's all.
Btw GetFileSize is no good for certain hidden files (CreateFile fails), as I noted when testing for a large file:
include \masm32\MasmBasic\MasmBasic.inc
Init
.if Exist("C:\hiberfil.sys")
Inkey Str$("File size is %3f GB", edx::GfSize(-1)/1073741824)
.else
Inkey "no such file"
.endif
EndOfCode
File size is 4.39 GB (GfSize (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1061) uses FindFirstFile instead of GetFileSize)
jj
i think youve prob misunderstood what i wrote .. we can all see the params are incorrect ..
1) the question is whether create file succeeded with apparantly erroneous params [ hence the "not defined" comment ]
id like to see what happens with invalidity testing in to see if it did work by chance
2) the file size if created implied a fairly substantial file overhead hence the first comment i made
interested to hear about getfile though
regards mikeb
For large files that are indeed accessible, the correct use of CreateFile+GetFileSize is as follows:
mov hTheFile, rv(CreateFileA, Chr$("GetFileSize.asc"), GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
jinvoke GetFileSize, hTheFile, addr fsize ; point to a DWORD
mov edx, fsize
shl rdx, 32
add rax, rdx
Print Str$("The file size is %i bytes\n", rax)
As you rightly write, Mike, there is a certain overhead: CreateFile(); that's why GfSize() (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1061) uses FindFirstFile under the hood, and can check the size of locked files, too.
How relevant that overhead is, that's another question :cool:
25 ms for 1000x CreateFile, GetFileSize and CloseHandle
25 ms for 1000x FindFirstFile and getting the size
22 ms for 1000x CreateFile, GetFileSize and CloseHandle
25 ms for 1000x FindFirstFile and getting the size
20 ms for 1000x CreateFile, GetFileSize and CloseHandle
24 ms for 1000x FindFirstFile and getting the size
21 ms for 1000x CreateFile, GetFileSize and CloseHandle
24 ms for 1000x FindFirstFile and getting the size
21 ms for 1000x CreateFile, GetFileSize and CloseHandle
25 ms for 1000x FindFirstFile and getting the size
The createfile function don't give the size of the file.What you get is unitialised data.
use : FindFirstFile to get the size
Quote
.data
InfosFiles WIN32_FIND_DATA <>
NomAediter db "\chose\myfle.txt",0
.code
invoke FindFirstFile,addr NomAediter,addr InfosFiles
mov edx,ex
invoke FindClose,edx ;close the search and now you have the size of the file in InfosFiles.nFileSizeLow
Timo,
Quote from: TimoVJL on February 10, 2021, 11:55:08 PM
uint64_format db "%llu",0 ; 64 bit unsigned integer
this might not be usable with msvcrt.dll,
have to use at least msvcr100.dll ?
you're right. It works fine with GCC's RTL, but not with msvcrt.dll. That's a real mess.
Erol,
Quote from: Vortex on February 10, 2021, 11:58:39 PM
Can you try this one ?
invoke vc_printf, addr uint64_format, TheFileLen
that doesn't help.
HSE,
[/quote]
Quote from: HSE on February 11, 2021, 12:17:12 AM
A good practice (at least when something went wrong :biggrin: ) is:invoke CreateFileA, addr TheFile, GENERIC_READ, 0, 0, 0, FILE_ATTRIBUTE_NORMAL, OPEN_EXISTING
.if eax!=INVALID_HANDLE_VALUE
mov hTheFile, rax ; save file handle
...
.else
; there is an error
.endif
Regards, HSE
yes, of course. But it's only a small test program, to show the problem. Right now, it looks like it's a mix of several factors. I'll get back when I know more.
Gunther
As I already suspected, it was a concatenation of various error causes. Once the order of the parameters was not correct. On the other hand, there were problems printing unsigned long long with msvcrt.dll.
I was able to correct all this and now the file length is correctly determined and printed. Thanks for the help.
Gunther
Quote from: Gunther on February 11, 2021, 03:31:45 AM
It works fine with GCC's RTL, but not with msvcrt.dll. That's a real mess.
M$ just wanted to do things in their own way in Steve Pallmer era, C99 standard wasn't in their list.
Timo,
Quote from: TimoVJL on February 11, 2021, 10:59:05 PM
M$ just wanted to do things in their own way in Steve Pallmer era, C99 standard wasn't in their list.
that's right, but you can't just blame Ballmer, the gnat. Mr. Gates also has his share in this development.
Gunther
Hi Gunther,
Reading this message :
http://masm32.com/board/index.php?topic=2769.msg29205#msg29205
Could you try the size prefixe %I64u ?
include \masm32\include64\masm64rt.inc
EXTERN ParseCmdLine:PROC
.data
msg1 db 'Size of the file %s = %I64u',13,10,0
msg2 db 'GetFileSize.exe filename.ext',13,10,0
.data?
cmdline db 512 dup(?)
.code
start PROC
call main
invoke ExitProcess,0
start ENDP
main PROC
LOCAL .rsi:QWORD
LOCAL hFile:QWORD
LOCAL lpFileSizeHigh:QWORD
mov .rsi,rsi
mov rsi,OFFSET cmdline
invoke ParseCmdLine,rsi
cmp rax,2
je @f
invoke vc_printf,ADDR msg2
ret
@@:
invoke CreateFile,QWORD PTR [rsi+8],\
GENERIC_READ,\
0,0,OPEN_EXISTING,0,0
mov hFile,rax
lea rdx,lpFileSizeHigh
invoke GetFileSize,rax,rdx
mov rdx,lpFileSizeHigh
shl rdx,32
add rax,rdx
invoke vc_printf,ADDR msg1,\
QWORD PTR [rsi+8],rax
mov rsi,.rsi
ret
main ENDP
END
Erol,
Quote from: Vortex on February 12, 2021, 04:22:23 AM
Could you try the size prefixe %I64u ?
yes, but it didn't work. Here is the output:
Value as unsigned long long = 5368717488
Value as long long = 5368717488
Press any key to continue...
The source and EXE is attached in the zip archive.
Quote from: Vortex on February 12, 2021, 04:22:23 AM
Reading this message :
http://masm32.com/board/index.php?topic=2769.msg29205#msg29205
Interesting thread, by the way. Why didn't Jochen mention that one?
Gunther
Quote from: Gunther on February 12, 2021, 09:59:16 AM
Interesting thread, by the way. Why didn't Jochen mention that one?
> googling for "llu" format specifier "msvcrt.dll" reveals that it's a KnownBug in a KnownDLL ;-)
That's over 7 years ago, I can't remember all the times when we tortured the CRT :biggrin:
Hi Gunther,
Replacing ADDR Value with only Value :
invoke vc_printf,ADDR uint64_format,Value ; ADDR Value
invoke vc_printf,ADDR str_format,ADDR NL
; print as long long
invoke vc_printf,ADDR str_format,ADDR msg1
invoke vc_printf,ADDR int64_format,Value ; ADDR Value
Building and running the application :
Value as unsigned long long = 18446744073709551615
Value as long long = -1
Press any key to continue...
a sample in 64:
Copy paste the full path of the file (console mode)
include sdk64.inc
include stdio.sdk
include conio.sdk
;https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
;-c -nologo -coff -Zi4 -Zd
.LISTALL
.const
;------------------------------------MACROS--------------------------------------------------
PuPo MACRO M1,M2
Push M2
Pop M1
ENDM
PASSESPACETAB MACRO register
@@:
.if byte ptr [register] == " " || byte ptr [register] == 9
inc register
jmp @B
.endif
ENDM
;----------------------------------------------------------------------------------------------
.data
;--------------------------------------------------------------------------------------------------------------------
InfosFichiers WIN32_FIND_DATA <>
;key data
;--------------------------------------------
chaine db MAX_PATH dup(0)
szfile db MAX_PATH dup(0)
;-------------------------------------------------------------------------------------------------------------------
.code
include affiche_error_msg.inc
Import_File PROC FRAME
Local Hsearch:QWORD,retour:QWORD
mov retour,0
invoke printf,TXT(" Copy paste the name of the file (full path) : ")
invoke scanf,TXT("%s"),addr chaine
lea r11,chaine
PASSESPACETAB r11
invoke lstrcpy,addr szfile,R11
;test if the file exist and get the size
invoke FindFirstFile,addr szfile,addr InfosFichiers
mov Hsearch,rax
.if rax != INVALID_HANDLE_VALUE
invoke FindClose,Hsearch
mov retour,1
.endif
mov rax,retour
ret
Import_File endp
main proc ;c(32) or not c(64 fastcall) must be tested,not using main(standard entry crt) can need additionnal libraries
invoke Import_File
.if rax == 1
invoke printf,TXT(" Size of the file %u "),InfosFichiers.nFileSizeLow
.else
invoke printf,TXT(" File not found ")
.endif
invoke _getch
mov rax,0
ret
main endp
end
Thanks Yves.
Quote from: TouEnMasm on February 12, 2021, 08:56:48 PM
a sample in 64:
Copy paste the full path of the file (console mode)
At the moment it is about the errors of the CRT when printing unsigned long long and long long.
Gunther
Erol,
Quote from: Vortex on February 12, 2021, 06:44:20 PM
Replacing ADDR Value with only Value :
that works fine. But that's a little strange, isn't it.
Jochen,
Quote from: jj2007 on February 12, 2021, 11:33:46 AM
> googling for "llu" format specifier "msvcrt.dll" reveals that it's a KnownBug in a KnownDLL ;-)
That's over 7 years ago, I can't remember all the times when we tortured the CRT :biggrin:
in other words, you're just like me - becoming forgetful. This is quite normal in old age. :cool:
Gunther
I have not been following this topic in detail but here is a quick test piece that drops the correct number for a file larger than 4 gig.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include64\masm64rt.inc
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
LOCAL hFile :QWORD
LOCAL flen :QWORD
LOCAL hiwd :QWORD
LOCAL lowd :QWORD
mov hFile, flopen("m:\$big\bigger.mp4") ; 5,940,221,382 bytes
invoke GetFileSize,hFile,ptr$(hiwd)
mov flen, rax
conout str$(flen),lf,lf
conout str$(hiwd),lf,lf
mov rax, flen
mov rcx, 4294967296
add rax, rcx ; add 4 gig
conout str$(rax),lf
flclose hFile
waitkey
.exit
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
Steve,
Quote from: hutch-- on February 12, 2021, 11:52:29 PM
I have not been following this topic in detail but here is a quick test piece that drops the correct number for a file larger than 4 gig.
yes, I've made a similar approach. conout (mighty macro) will do the job. Is it possible to print with conout long long integers, too?
Gunther
Here is a PROC version that seems to work OK.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include64\masm64rt.inc
.code
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
entry_point proc
rcall LargeFileSize,"m:\$big\bigger.mp4"
conout str$(rax),lf
waitkey
.exit
entry_point endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
LargeFileSize proc pName:QWORD
USING r12
LOCAL hFile :QWORD
; LOCAL flen :QWORD
LOCAL hiwd :QWORD
; LOCAL lowd :QWORD
SaveRegs
mov hFile, flopen(pName)
invoke GetFileSize,hFile,ptr$(hiwd)
cmp hiwd, 0
je past
mov rcx, 4294967296 ; on the fly optimisation :)
@@:
add rax, rcx ; add 4 gig each iteration
sub hiwd, 1
jnz @B
past:
mov r12, rax ; preserve rax
flclose hFile
mov rax, r12 ; restore rax
RestoreRegs
ret
LargeFileSize endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end
for big files and using long long
BOOL GetFileSizeEx( HANDLE hFile, PLARGE_INTEGER lpFileSize);
Quote from: TimoVJL on February 13, 2021, 12:27:09 AM
for big files and using long long
BOOL GetFileSizeEx( HANDLE hFile, PLARGE_INTEGER lpFileSize);
Good find :thumbsup:
However, some of the really big files are system files that CreateProcess can't handle, so FindFirst* is still the better choice...
Quote from: jj2007 on February 11, 2021, 12:48:08 AMGetFileSize is no good for certain hidden files (CreateFile fails), as I noted when testing for a large file:
include \masm32\MasmBasic\MasmBasic.inc
Init
.if Exist("C:\hiberfil.sys")
Inkey Str$("File size is %3f GB", edx::GfSize(-1)/1073741824)
.else
Inkey "no such file"
.endif
EndOfCode
File size is 4.39 GB (GfSize (https://www.jj2007.eu/MasmBasicQuickReference.htm#Mb1061) uses FindFirstFile instead of GetFileSize)
:biggrin:
Never could resist the temptation to optimise an algo.
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
LargeFileSize proc pName:QWORD
USING r12, r13 ; specify the registers to preserve
LOCAL hiwd :QWORD ; allocate a LOCAL memory operand
SaveRegs ; save the regs specified in USING
mov r13, flopen(pName) ; r13 is the file handle
invoke GetFileSize,r13,ptr$(hiwd)
cmp hiwd, 0 ; if hiwd = 0, bypass loop
je past
mov rcx, 4294967296 ; load rcx with a 4 gig immediate
@@:
add rax, rcx ; add 4 gig each iteration
sub hiwd, 1
jnz @B
past:
mov r12, rax ; preserve rax
flclose r13 ; close the file
mov rax, r12 ; restore rax
RestoreRegs ; restore the regs specified in USING
ret
LargeFileSize endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Hi Gunther,
There is nothing strange. You would like to print the value of the variable named Value not it's address. Disassembling the object module :
lea rcx, [str_format]
lea rdx, [msg0]
call qword ptr [__imp_printf]
lea rcx, [uint64_format]
mov rdx, qword ptr [Value]
call qword ptr [__imp_printf]
lea rcx, [str_format]
lea rdx, [NL]
call qword ptr [__imp_printf]
lea rcx, [str_format]
lea rdx, [msg1]
call qword ptr [__imp_printf]
lea rcx, [int64_format]
mov rdx, qword ptr [Value]
call qword ptr [__imp_printf]
lea rcx, [str_format]
lea rdx, [NL]
call qword ptr [__imp_printf]
Printing Value :
invoke vc_printf,ADDR uint64_format,Value
This will not work because it prints the address of the variable Value :
invoke vc_printf,ADDR uint64_format,ADDR Value
Erol,
Quote from: Vortex on February 13, 2021, 12:49:53 AM
Printing Value :
invoke vc_printf,ADDR uint64_format,Value
This will not work because it prints the address of the variable Value :
invoke vc_printf,ADDR uint64_format,ADDR Value
you're right. It was my thought error.
Gunther
Quote
you're right. It was my thought error.
No!,if the error is here,this one is given by the modified msvcrt.
In 32 and 64 bits,the printf function (used by vc_printf) pass it's argument by values not by adress.
It is the vc_printf function who is wrong.
The sample(s) i have posted,use the printf function and you can verify this.
Quote
printf PROTO cproto :XMASM , :VARARG
;cproto is c in 32 bits and fastcall in 64
Yves,
If you bothered to look in the 32 bit "msvcrt.inc" file you would see how its done properly.
; ------------------------------------------
; prototypes for EXPORT msvcrt functions
; ------------------------------------------
c_msvcrt typedef PROTO C :VARARG
........
externdef _imp__printf:PTR c_msvcrt
crt_printf equ <_imp__printf>
Calling the function still requires passing the right arguments to it.
In the given sample in
64 bits,value is definad as follow:
Quote
Value dq -1 ; number to print
uint64_format db "%I64u", 0 ; unsigned long long
using the official msvcrt and printf to show value,write it like that
Quote
invoke printf, addr uint64_format,value,value ;not addr value
Where is the error ?
Quote from: TouEnMasm on February 13, 2021, 08:18:04 PM
using the official msvcrt and printf to show value,write it like that
Quote
invoke printf, addr uint64_format,value,value ;not addr value
Where is the error ?
That the correct way. Mentioned already in my message :
http://masm32.com/board/index.php?topic=9186.msg100864#msg100864
include \Masm32\MasmBasic\Res\JBasic.inc ; ## builds in 32- or 64-bit mode with UAsm, ML, AsmC ##
MyQword dq 12345678901234567890
Init ; OPT_64 1 ; put 0 for 32 bit, 1 for 64 bit assembly
jinvoke crt_printf, Chr$("The value is %I64u"), MyQword
Inkey Chr$(13, 10, 10, "This program was assembled with ", @AsmUsed$(1), " in ", jbit$, "-bit format.")
EndOfCode
The value is 12345678901234567890
This program was assembled with ml64 in 64-bit format.
This is the correct prototype for 64 bit MASM.
externdef __imp_printf:PPROC
vc_printf equ <__imp_printf>
If Yves wants to use the C library, it involves any extra overhead from doing so. In 64 bit MASM the libraries DO NOT HAVE the linker argument count that the 32 bit versions have.