Here are my simple file handling functions coded with Poasm. Masm and Poasm are using the same object file format, so the assembled procedures can be used with Masm.
ReadFileToMem :
WriteFileToDisc :
FreeMemory :
ReadFileToMem :
.386
.model flat,stdcall
option casemap:none
include filefunc.inc
READFILE_MEMINFO STRUCT
hHeap DWORD ?
hMem DWORD ?
FileSize DWORD ?
READFILE_MEMINFO ENDS
PUBLIC ReadFileToMem@8
.code
; ReadFileToMem PROC pFileName:DWORD,pMemInfo:DWORD
; pFileName : pointer to name of the file
; pMemInfo : pointer to structure for allocated memory information
; Return values : if the function succeeds, eax is NULL.
; if the function fails, the return value contains the error code
ReadFileToMem@8:
sub esp,2*4 ; reserve 8 bytes for the local variables
push esi
mov esi,DWORD PTR
invoke GetProcessHeap
test eax,eax
jne @f
mov eax,1
jmp error
@@:
mov READFILE_MEMINFO.hHeap,eax ; save the handle of the process heap
invoke CreateFile,DWORD PTR ,GENERIC_READ,0,0,\
OPEN_EXISTING,FILE_ATTRIBUTE_ARCHIVE,0
cmp eax,INVALID_HANDLE_VALUE
jne @f
mov eax,2
jmp error
@@:
mov DWORD PTR ,eax ; save the handle of file
invoke GetFileSize,eax,0
mov READFILE_MEMINFO.FileSize,eax
invoke HeapAlloc,READFILE_MEMINFO.hHeap,HEAP_ZERO_MEMORY,eax
mov READFILE_MEMINFO.hMem,eax ; save the address of the allocated memory
lea ecx,DWORD PTR ; get the address of number of bytes read
invoke ReadFile,DWORD PTR ,eax,READFILE_MEMINFO.FileSize,ecx,0
test eax,eax
jnz @f
mov eax,3
jmp error
@@:
invoke CloseHandle,DWORD PTR
xor eax,eax
error:
pop esi
add esp,2*4 ; balance the stack
ret 2*4
; ENDP ReadFileToMem
END
WriteFileToDisc :
.386
.model flat,stdcall
option casemap:none
include filefunc.inc
PUBLIC WriteFileToDisc@12
.code
; WriteFileToDisc PROC pFileName:DWORD,pMemory:DWORD,nSize:DWORD
; pFileName : pointer to name of the file
; pMemory : address of buffer to write file
; nSize : number of bytes to write
; Return values : if the function succeeds, eax is non-NULL
; : if the function fails, the return value is NULL
WriteFileToDisc@12:
sub esp,2*4 ; reserve 8 bytes for the local variables
invoke CreateFile,DWORD PTR ,GENERIC_WRITE,0,0,\
CREATE_ALWAYS,FILE_ATTRIBUTE_ARCHIVE,0
cmp eax,INVALID_HANDLE_VALUE
jne @f
xor eax,eax
jmp finish
@@:
mov DWORD PTR ,eax ; save the handle of file
lea ecx,DWORD PTR ; get the address of number of bytes written
invoke WriteFile,eax,DWORD PTR ,DWORD PTR ,ecx,0
test eax,eax
jz finish
invoke CloseHandle,DWORD PTR
finish:
add esp,2*4 ; balance the stack
ret 3*4
; ENDP WriteFileToDisc
END
FreeMemory :
.386
.model flat,stdcall
option casemap:none
HeapFree PROTO :DWORD,:DWORD,:DWORD
READFILE_MEMINFO STRUCT
hHeap DWORD ?
hMem DWORD ?
FileSize DWORD ?
READFILE_MEMINFO ENDS
PUBLIC FreeMemory@4
.code
; FreeMemory PROC pMemInfo
; pMemInfo : pointer to structure for allocated memory information
FreeMemory@4:
mov eax,DWORD PTR
invoke HeapFree,READFILE_MEMINFO.hHeap,0,READFILE_MEMINFO.hMem
ret 4
; ENDP FreeMemory
END
A bit of (constructive, hopefully) criticism:
EAX is 0, not NULL, on error... you really should only talk about NULL when dealing with pointer values.
Why are you using HEAP_ZERO_MEMORY when you're going to fill the entire allocated memory with data read from file? Waste of CPU cycles :)
Why are you using a hHeap when you only support using the process default heap?
It's probably a better idea to do away with the heap and use VirtualAlloc instead, unless most files read this way will be <4kb. And if you're dealing with a lot of small files, perhaps you should use a separate heap to reduce fragmentation of the main heap...
EAX is 0, not NULL, on error... you really should only talk about NULL when dealing with pointer values.
Why are you using HEAP_ZERO_MEMORY when you're going to fill the entire allocated memory with data read from file? Waste of CPU cycles :)
Why are you using a hHeap when you only support using the process default heap?
It's probably a better idea to do away with the heap and use VirtualAlloc instead, unless most files read this way will be <4kb. And if you're dealing with a lot of small files, perhaps you should use a separate heap to reduce fragmentation of the main heap...
Since asm does not support type casting like ((void *)0) - the ANSI definition of NULL, I assume that NULL is equal to 0 for practical programming. You can think that it's a kind of simulation of NULL pointer. Normally, the NULL pointer and 0 are not the same thing but this assumption should be a solution to make easier the life. You can think it like this one : engineering calculations are always an approximation as they cannot give exact results like in mathematics.
I am accustomed to use HEAP_ZERO_MEMORY. You are right, I can replace it with HEAP_GENERATE_EXCEPTIONS
hHeap is preserved with the intention to limit the call to GetProcessHeap to one time. The user would like to use hHeap to allocate another block of memory. My FreeMemory function also uses this value to release the allocated memory block.
It's in my mind to create another version of ReadFileToMem using VirtualAlloc. The heap functions are OK for small files and practical programming.
I am accustomed to use HEAP_ZERO_MEMORY. You are right, I can replace it with HEAP_GENERATE_EXCEPTIONS
hHeap is preserved with the intention to limit the call to GetProcessHeap to one time. The user would like to use hHeap to allocate another block of memory. My FreeMemory function also uses this value to release the allocated memory block.
It's in my mind to create another version of ReadFileToMem using VirtualAlloc. The heap functions are OK for small files and practical programming.
Since asm does not support type casting like ((void *)0) - the ANSI definition of NULL, I assume that NULL is equal to 0 for practical programming. You can think that it's a kind of simulation of NULL pointer. Normally, the NULL pointer and 0 are not the same thing but this assumption should be a solution to make easier the life. You can think it like this one : engineering calculations are always an approximation as they cannot give exact results like in mathematics.
Yes, NULL is equal to 0 (and for ISO C++, actually defined as 0 instead of the ANSI-C pointer type), but you have to consider the semantic value of using NULL - it's traditionally been used when dealing with pointer values, so when seeing a NULL, most people will expect that the argument is a pointer. Using NULL for zero integers is plain confusing.
You could also use NULL instead of MB_OK, you could use POWER_ACTION_CRITICAL instead of GENERIC_READ, etc... do you catch my drift? :)
I am accustomed to use HEAP_ZERO_MEMORY. You are right, I can replace it with HEAP_GENERATE_EXCEPTIONS
Just pass a 0 instead, indicating you don't need any special options. Using HEAP_GENERATE_EXCEPTIONS would require you to set up exception-handling code...
hHeap is preserved with the intention to limit the call to GetProcessHeap to one time. The user would like to use hHeap to allocate another block of memory. My FreeMemory function also uses this value to release the allocated memory block.
This is pretty much a non-optimization, GetProcessHeap is a very inexpensive call, instead you waste a bit of memory for every open file. Okay, I can't think of any scenario where the memory would add up enough to be a problem, but as long as you're only using the default process heap, there isn't much use in storing hHeap.
It's in my mind to create another version of ReadFileToMem using VirtualAlloc. The heap functions are OK for small files and practical programming.
Yes, the heap is good for small allocations, that's what is what meant for. And VirtualAlloc rounds your size allocations up to 4kb, as well as aligning the memory to 64kb boundaries, so it's only good when you're dealing with larger allocations... but in that situation, I'd choose it over heap functions any time, any day.
Anyway, hope you don't take this as your code being bashed, it's a few relatively minor points on the way to perfection :)
This is pretty much a non-optimization, GetProcessHeap is a very inexpensive call, instead you waste a bit of memory for every open file. Okay, I can't think of any scenario where the memory would add up enough to be a problem, but as long as you're only using the default process heap, there isn't much use in storing hHeap.
Storing hHeap should be more faster than calling repeatly GetProcessHeap Optimizing every line of this routine does not worth much, very few people would be interested in a super-optimized ReadFile function. Anyway, calling GetProcessHeap of stroring the value should not be an issue here.
I have no problem with your comments.
Storing hHeap should be more faster than calling repeatly GetProcessHeap Optimizing every line of this routine does not worth much, very few people would be interested in a super-optimized ReadFile function. Anyway, calling GetProcessHeap of stroring the value should not be an issue here.
Slightly faster, yes, but it's a micro-optimization that's pretty pointless; all GetProcessHeap does is giving you a value from the TEB, no ring3<>ring0 stuff going on. For the sake of completeness and considering how simple the function is, I suppose it's okay to post the disassembly here:
.text:7D4D8DF9 GetProcessHeap proc near
.text:7D4D8DF9 mov eax, large fs:18h
.text:7D4D8DFF mov eax,
.text:7D4D8E02 mov eax,
.text:7D4D8E05 retn
.text:7D4D8E05 GetProcessHeap endp
So sure, it's slightly faster not calling it, but you'll have a hard time measuring any performance gain. It's funny that you say "very few people would be interested in a super-optimized ReadFile function", but still have applied a micro-optimization like this :)