News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

WriteFile error

Started by bf2, December 01, 2011, 11:46:04 PM

Previous topic - Next topic

bf2

I am getting the error "The supplied user buffer is not valid for the requested operation" after the call to WriteFile in the code below. I am just trying to read a file, write it to the console and then copy it into another file. All works OK except for the copying bit.


INCLUDE \masm32\include\masm32rt.inc

.DATA
hInputFile DD ?
hOutputFile DD ?
fileSize DD ?
numRead DD ?
numWritten DD ?
inputFile DB "Test.asm", 0
outputFile DB "Copy.asm", 0
hStdOut DD ?
hMemory DD ?
errorNum DD ?
errorBuffer DB MAX_PATH DUP(?)

.CODE
start:
INVOKE GetStdHandle, STD_OUTPUT_HANDLE ; -11
MOV hStdOut, EAX

    INVOKE CreateFile, \
    OFFSET inputFile, \
    GENERIC_READ, \
    FILE_SHARE_READ, \
    NULL, \
    OPEN_EXISTING, \
    FILE_ATTRIBUTE_NORMAL, \
    NULL
MOV hInputFile, EAX
CMP hInputFile, INVALID_HANDLE_VALUE
JE errorProcess

    INVOKE CreateFile, \
    OFFSET outputFile, \
    GENERIC_WRITE, \
    0, \
    NULL, \
    CREATE_ALWAYS, \
    FILE_ATTRIBUTE_NORMAL, \
    NULL
MOV hOutputFile, EAX
CMP hOutputFile, INVALID_HANDLE_VALUE
JE errorProcess

INVOKE GetFileSize, hInputFile, NULL
MOV fileSize, EAX

INVOKE GlobalAlloc, GMEM_FIXED, fileSize
    MOV hMemory, EAX
    CMP hMemory, 0
    JE errorProcess

INVOKE ReadFile, \
hInputFile, \
ADDR hMemory, \
      fileSize, \
      ADDR numRead, \
      0
CMP EAX, 0
JE errorProcess

INVOKE WriteConsole, hStdOut, OFFSET hMemory, fileSize, numWritten, 0

INVOKE WriteFile, \
hOutputFile, \
hMemory, \
fileSize, \
ADDR numWritten, \
NULL
CMP EAX, 0
JE errorProcess

JMP finalise

errorProcess:
INVOKE GetLastError
INVOKE FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM, 0, EAX, 0, ADDR errorBuffer, MAX_PATH, 0
INVOKE WriteConsole, hStdOut, OFFSET errorBuffer, MAX_PATH, numWritten, 0

finalise:
INVOKE GlobalFree, hMemory
INVOKE CloseHandle, hInputFile
INVOKE CloseHandle, hOutputFile
INVOKE ExitProcess, 0

END start


jj2007

addr hMemory and offset hMemory - not the address of the variable, but the variable hMemory itself is needed.
Furthermore, you should not write more than, say, 32k to the console - see WriteFile doc, Community additions:
mov edx, fileSize
.if edx>32768
mov edx, 32768
.endif
INVOKE WriteConsole, hStdOut, hMemory, edx, addr numWritten, 0

hutch--

JJ is correct here, if you want to write large amounts of text to the console you need to chop it up into smaller bits, 10k seems to work OK and if you use StdOut style output you can redirect it to a file with standard console notation.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

bf2

Quote from: jj2007 on December 01, 2011, 11:59:38 PM
addr hMemory and offset hMemory - not the address of the variable, but the variable hMemory itself is needed.

jj, strangely enough, I have now managed to make the program work using OFFSET on both the WriteConsole and WriteFile calls.

So, the following works:

INVOKE WriteConsole, hStdOut, OFFSET hMemory, fileSize, numWritten, 0

INVOKE WriteFile, \
hOutputFile, \
OFFSET hMemory, \
fileSize, \
ADDR numWritten, \
NULL

I.e. the program writes on the console and also writes in the copy file.

If I remove the OFFSET only from the WriteFile call:

INVOKE WriteConsole, hStdOut, OFFSET hMemory, fileSize, numWritten, 0

INVOKE WriteFile, \
hOutputFile, \
hMemory, \
fileSize, \
ADDR numWritten, \
NULL

then it writes on the console fine, and creates the copy file but doesn't write any lines.

If I remove the OFFSET from the WriteConsole call, then the program crashes:


INVOKE WriteConsole, hStdOut, hMemory, fileSize, numWritten, 0

INVOKE WriteFile, \
hOutputFile, \
hMemory, \
fileSize, \
ADDR numWritten, \
NULL


I am not quite sure what is happening.

PS. I take your and Hutch's point about size.

jj2007

Quote from: bf2 on December 02, 2011, 07:15:39 AM
Quote from: jj2007 on December 01, 2011, 11:59:38 PM
addr hMemory and offset hMemory - not the address of the variable, but the variable hMemory itself is needed.

jj, strangely enough, I have now managed to make the program work using OFFSET on both the WriteConsole and WriteFile calls.

Congrats. How big is your test.asm? Have you tried with something a little bit bigger than MAX_PATH?
Hint: Your program works even if you comment out the GlobalAlloc part, like this:

; INVOKE GlobalAlloc, GMEM_FIXED, fileSize
;    MOV hMemory, EAX
;    CMP hMemory, 0
;    JE errorProcess


.data
...
hMemory DD ?
errorNum DD ?
errorBuffer DB MAX_PATH DUP(?)

.CODE

hutch--

Here is how to stream any sized file to the cxonsole.


IF 0  ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
                      Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    include \masm32\include\masm32rt.inc

    .data?
      var dd ?

    .data
      winc db "\masm32\include\windows.inc",0

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main
    inkey
    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    LOCAL hFile :DWORD      ; file handle
    LOCAL bcnt  :DWORD      ; read block size
    LOCAL pMem  :DWORD      ; memory handle
    LOCAL hOut  :DWORD
    LOCAL bWrt  :DWORD

    mov bcnt, 8192          ; set your buffer size

  ; -------------------------
  ; allocate that much memory
  ; -------------------------
    invoke GlobalAlloc,GMEM_FIXED,bcnt
    mov pMem, eax

  ; --------------------------------
  ; open the file and get its handle
  ; --------------------------------
    invoke CreateFile,ADDR winc,GENERIC_READ or GENERIC_WRITE,
                        NULL,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL
    mov hFile, eax      ; get handle to open file

    invoke GetStdHandle,STD_OUTPUT_HANDLE
    mov hOut, eax

  @@:
  ; ---------------------------------------
  ; read the bcnt buffer size from the file
  ; ---------------------------------------
    invoke ReadFile,hFile,pMem,bcnt,ADDR var,NULL

  ; ----------------------------------------------------
  ; write the actual number of bytes read to the console
  ; ----------------------------------------------------
    fn WriteConsole,hOut,pMem,var,ADDR bWrt,NULL

    mov eax, var            ; load "var" into EAX

    .if eax == bcnt         ; if "var" is equal to bcnt
      jmp @B                ; loop back and read next block
    .endif

    invoke CloseHandle,hFile
    invoke GlobalFree,pMem

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

dedndave

i was playing with this some time ago
i found the limit to be something in the 55 kb neighbourhood (XP)
if you wrote less than the limit, it worked fine
if you wrote more than the limit, nothing happened - lol
odd size - the 55 kb - i forget the exact number - it probably varies from OS to OS - i don't know
but - 32 kb is reasonably safe
let's face it - if you are dumping that much to the console, you aren't going to see most of it, anyways

KeepingRealBusy

Dave,

That may be because of some console restriction, but when copying files using unbuffered I/O, I use 64KB all of the time with no error. You need buffers that are mod sector size and need to do sector sized reads and writes. Use VirualAlloc to get the buffers and you get such buffers. When reading or writing the final piece (if it is not mod sector size), read or write mod sector sized pieces, then close the file and open it as buffered, position the file to the end of the last piece read or written, then read or write the final fractional piece.

Dave.

jj2007

Quote from: dedndave on December 02, 2011, 04:38:41 PM
i found the limit to be something in the 55 kb neighbourhood (XP)

53216 bytes :bg

Quote   mov ebx, fileSize
   .if ebx>60000
      mov ebx, 60000
   .endif
   .Repeat
      INVOKE   WriteConsole, hStdOut, hMemory, ebx, addr numWritten, 0
      dec ebx
   .Until eax
   Print Str$("\n\nFirst good result: %i\n\n\n", ebx)

dedndave

oh yes - i know you can use larger buffers for things other than console output
i just thought it was an odd boundry, is all - lol

of course - it's just one more bug that the console has
for some time, i have learned to view the console as a "test tool", more or less
it has it's uses - sure
but, it's no place for any kind of application that uses a "dynamic" display
i.e. one where you reposition the cursor, rather that tty scrolling the output text
as we can see, it doesn't even do that right   :bg

bf2

Quote from: jj2007 on December 02, 2011, 08:23:43 AM
Quote from: bf2 on December 02, 2011, 07:15:39 AM
Quote from: jj2007 on December 01, 2011, 11:59:38 PM
addr hMemory and offset hMemory - not the address of the variable, but the variable hMemory itself is needed.

jj, strangely enough, I have now managed to make the program work using OFFSET on both the WriteConsole and WriteFile calls.

Congrats. How big is your test.asm? Have you tried with something a little bit bigger than MAX_PATH?
Hint: Your program works even if you comment out the GlobalAlloc part, like this:

; INVOKE GlobalAlloc, GMEM_FIXED, fileSize
;    MOV hMemory, EAX
;    CMP hMemory, 0
;    JE errorProcess


.data
...
hMemory DD ?
errorNum DD ?
errorBuffer DB MAX_PATH DUP(?)

.CODE


jj, sorry I am being thick but I am still not clear about this. I appreciate your comment about file size, but I cannot understand why the program works even when you have commented out the GlobalAllock block and why my previous version worked when I used the address of hMem as opposed to hMem itself.

Please help.

jj2007

Quote from: bf2 on December 02, 2011, 08:59:21 PM

I cannot understand why the program works even when you have commented out the GlobalAllock block and why my previous version worked when I used the address of hMem as opposed to hMem itself.


You pass a pointer to a buffer to ReadFile and WriteFile.
If you pass hMemory, the two functions will use the memory that you got with GlobalAlloc.
If you pass addr hMemory, the two functions will use ... the .data section, i.e. everything that comes after hMemory. And Bang! with files larger than some kBytes...
.DATA
...
hMemory DD ?
errorNum DD ?
errorBuffer DB MAX_PATH DUP(?)


Working correct version attached.

bf2

Ah, got it now. Thanks very much.