Сборка x86 MASM - входной буфер содержит старый ввод, несмотря на FlushConsoleInputBuffer

Чтобы попрактиковаться в сборке в MASM, я создал небольшую программу, которая должна делать следующее:

  1. Напечатайте "Type a:" на экране
  2. Прочитать один символ из входного буфера, который затем сбрасывается
  3. Если символ «а», то выйдите из цикла и завершите программу, в противном случае повторите с первого шага.

Мой код выглядит следующим образом:

.386


.model flat,stdcall


include \masm32\include\kernel32.inc ; Defines Symbols To Be Used for the kernel32 library


includelib \masm32\lib\kernel32.lib



STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10

.code



entryPt proc
    local inHandle:DWORD
    local outHandle:DWORD
    local noOfCharsWritten:DWORD
    ; Get Standard Output Handle
    push STD_OUTPUT_HANDLE
    call GetStdHandle
    mov outHandle,eax

    ; Get Standard Input Handle
    push STD_INPUT_HANDLE
    call GetStdHandle
    mov inHandle,eax

    .while (eax == eax) ; while (true)

        ; Print "Type a: "
        push 0
        lea eax,noOfCharsWritten
        push eax
        push sizeof txt
        push offset txt
        push outHandle
        call WriteConsoleA

        ; Poll for a byte
        call getChar

        .if (al == "a") ; if the byte was "a"...
            .break ; ...then end the loop
        .endif

    .endw

    ; Leave with exit code 0
    push 0
    call ExitProcess
entryPt endp

getChar proc
    local inHandle:DWORD
    local noOfCharsRead:DWORD
    local resBt:BYTE
    ; Get the Standard Input Handle
    push STD_INPUT_HANDLE
    call GetStdHandle
    mov inHandle,eax
    ; Read one char from the console, put the result in resBt and the number of chars read in noOfCharsRead
    push 0
    lea eax,noOfCharsRead
    push eax
    push 1
    lea eax,resBt
    push eax
    push inHandle
    call ReadConsoleA

    ; Flush Console Input Buffer
    push inHandle
    call FlushConsoleInputBuffer

    ; Return the result in the accumulator
    movzx eax,resBt
    ret
getChar endp
.data

txt db "Type a: "

end entryPt

При вводе «a» программа выйдет, как и должна. Однако, если я наберу что-нибудь, кроме «a» (например, «s»), вместо того, чтобы снова запрашивать «Type a:», только один раз, вместо этого будет написано «Type a: Type a: Type a:» перед запрос другого байта. Написание более одного символа, отличного от a, приведет к еще большему количеству "Type a:" s.

Я подозреваю, что это потому, что ReadConsole читает старый ввод и, следовательно, преждевременно завершает функцию, но не нужно ли FlushConsoleInputBuffer очистить старый ввод?


person Epsilon    schedule 08.12.2018    source источник


Ответы (1)


ReadConsole считывает все доступные символы из входного буфера консоли и сохраняет их в отдельном буфере, на который не влияет FlushConsoleInputBuffer. У вас нет прямого доступа к этому буферу и вы не можете получить о нем информацию. Таким образом, вы должны прочитать его с ReadConsole до конца строки. По умолчанию конец строки отмечен двумя байтами CR (0x0D) и LF (0x0A). Поскольку вы читаете только один байт, в буфере остается как минимум LF.

Замените FlushConsoleInputBuffer циклом ReadConsole, чтобы очистить буфер до тех пор, пока не будет прочитан LF:

.model flat,stdcall

include \masm32\include\kernel32.inc ; Defines Symbols To Be Used for the kernel32 library
includelib \masm32\lib\kernel32.lib

STD_OUTPUT_HANDLE equ -11
STD_INPUT_HANDLE equ -10

.code

entryPt proc
    local inHandle:DWORD
    local outHandle:DWORD
    local noOfCharsWritten:DWORD
    ; Get Standard Output Handle
    push STD_OUTPUT_HANDLE
    call GetStdHandle
    mov outHandle,eax

    ; Get Standard Input Handle
    push STD_INPUT_HANDLE
    call GetStdHandle
    mov inHandle,eax

    .while (eax == eax) ; while (true)

        ; Print "Type a: "
        push 0
        lea eax,noOfCharsWritten
        push eax
        push sizeof txt
        push offset txt
        push outHandle
        call WriteConsoleA

        ; Poll for a byte
        call getChar

        .if (al == "a") ; if the byte was "a"...
            .break ; ...then end the loop
        .endif

    .endw

    ; Leave with exit code 0
    push 0
    call ExitProcess
entryPt endp

getChar proc
    local inHandle:DWORD
    local noOfCharsRead:DWORD
    local resBt:BYTE, dummy:BYTE

    ; Get the Standard Input Handle
    push STD_INPUT_HANDLE
    call GetStdHandle
    mov inHandle,eax

    ; Read one char from the console, put the result in resBt and the number of chars read in noOfCharsRead
    push 0
    lea eax,noOfCharsRead
    push eax
    push 1
    lea eax,resBt
    push eax
    push inHandle
    call ReadConsoleA

    ; Flush
    mov al, resBt
    mov dummy, al
    FlushLoop:
    cmp dummy, 0Ah
    je EndOfFlush
    invoke ReadConsoleA, inHandle, ADDR dummy, 1, ADDR noOfCharsRead, 0
    jmp FlushLoop
    EndOfFlush:

    ; Return the result in the accumulator

    movzx eax,resBt
    ret
getChar endp
.data

txt db "Type a: "

end entryPt
person rkhb    schedule 08.12.2018
comment
Большое спасибо! - person Epsilon; 09.12.2018