News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Reading a char from console.

Started by p3tr0va, October 20, 2012, 11:15:48 AM

Previous topic - Next topic

p3tr0va

Hi
I have troubles trying to read only one char from the console.
When I type a char and press enter, the console input buffer stores
the first char and the CR and LF chars too.
I want to read only the first char, not the rest of the line.
So, I use the FlushConsoleInputBuffer function to remove the CR and LF
chars from the console buffer, but the CR and LF chars are still there, Why?


LENBUFFER=1

.data
buffer          db LENBUFFER+1 dup(0)
readBytes       dd ?
hConInput       HANDLE ?

.code
start:
    push 0
    call SetLastError

    push STD_INPUT_HANDLE
    call GetStdHandle
    mov  hConInput,eax

readChar:
    call Crlf ; print a CRLF
    mWrite "(0 to quit) Give me a char: "  ; macro to print text

    push 0
    push offset readBytes
    push 1            ; read only the first char
    push offset buffer
    push hConInput
    call ReadConsole
    cmp  eax,0
    je   error
   
    cmp  byte ptr[buffer],30h  ; stop condition
    je   exit
   
    mov  edx,offset buffer
    call WriteString         ; print the stored char
   
    push hConInput
    call FlushConsoleInputBuffer ; remove remaining chars
    cmp  eax,0
    je   error
   
    jmp  readChar   ; read another char
   
error:   
    call WriteWindowsMsg   ; print error messages

exit:
    push 0
    call ExitProcess
end start

dedndave

the carriage return signifies the "end of file" for the console

the console window is buggish - lol
the best results i have seen for this type of keyboard input is to use the CRT functions
here is a routine that i use and a program showing how it might be used
        .XCREF
        .NOLIST
        INCLUDE \masm32\include\masm32rt.inc
        .LIST

;#########################################################################

        .CODE

;*************************************************************************

_main   PROC

        print   chr$('Press Esc to Exit'),13,10
        jmp short kloop2

kloop1: INVOKE  Sleep,40

kloop2: call    InKyb
        jz      kloop1

        push    eax
        cmp     ah,0
        jz      kloop3

        push    2020h
        jmp short kloop4

kloop3: mov     ah,20h
        push    eax

kloop4: print   esp
        pop     edx
        pop     eax
        push    eax
        print   right$(uhex$(eax),4),13,10
        pop     eax
        cmp     eax,1Bh
        jnz     kloop2

        exit

_main   ENDP

;*************************************************************************

InKyb   PROC

;Polled Keyboard Input - DednDave 8, 2010
;
;This function returns a keystroke in EAX if there is one in the buffer.
;If the buffer is empty, the function returns immediately.
;
;If the keyboard buffer is empty, AH = 0, AL = 0, ZF = 1.
;If the stroke is a regular key, AH = 0, AL = key char, ZF = 0.
;If the stroke is an extended key, AH = extended key, AL = E0h, ZF = 0.
;If the stroke is a function key, AH = function key, AL = 0, ZF = 0.
;
;ECX, EDX are not preserved.

        call    crt__kbhit
        or      eax,eax
        jz      InKyb1

        call    crt__getch
        and     eax,0FFh
        jz      InKyb0

        cmp     al,0E0h
        jnz     InKyb1

InKyb0: push    eax
        call    crt__getch
        pop     edx
        shl     eax,8
        or      eax,edx

InKyb1: retn

InKyb   ENDP

;#########################################################################

        END     _main


hutch--

If its only one byte you are after and you want to use the API instead of the C runtime function, why not just read the first byte and ignore the trailing CR LF ?

jj2007

Modify according to your needs (if you prefer it complicated, use PeekConsoleInput or ReadConsoleInput).

include \masm32\include\masm32rt.inc

.code
start:
   .While 1
      getkey
      .Break .if eax==13   ; Cr aka Return
      push eax
      print esp
      pop eax
   .Endw
   exit
end start

Here is the Unicode version, for use with e.g. the €uro key or exotic keyboards:

include \masm32\MasmBasic\MasmBasic.inc   ; download

.code
start:
   .While 1
      Inkey
      .Break .if eax==13      ; Cr aka Return
      push eax             ; create a buffer and fill it with one zero-delimited byte
      wPrint esp
      pop eax
   .Endw
   exit
end start

TouEnMasm

Quote
I have troubles trying to read only one char from the console.
When I type a char and press enter, the console input buffer stores
the first char and the CR and LF chars too
The _getch function do that

Quote
Gets a character from the console without echo.

int _getch( void );
wint_t _getwch( void );
Return Value
Returns the character read. There is no error return.

Remarks
The _getch and_getwch functions read a single character from the console without echoing the character. None of these functions can be used to read CTRL+C. When reading a function key or an arrow key, each function must be called twice; the first call returns 0 or 0xE0, and the second call returns the actual key code

Fa is a musical note to play with CL

p3tr0va

Quote from: hutch-- on October 20, 2012, 04:14:43 PM
If its only one byte you are after and you want to use the API instead of the C runtime function, why not just read the first byte and ignore the trailing CR LF ?

I can't ignore the trailing CR LF because I need read from the console more than once.

This tends to become in a return of this forum
http://www.programmersheaven.com/mb/windows/265472/265484/re-problem-with-console-app/?S=B20000
I have the same problem, but in assembly language.
By the way, the last answer in that forum didn't work for me.
Looks like the FlushConsoleInputBuffer function doesn't work really.
It's that right?

dedndave

one solution might be....

write a routine that reads all the bytes into a buffer
then, parse the buffer, converting the first carriage return or line feed into a null (0) byte
that way, the routine always returns a null-terminated string with the cr/lf stripped off
the function can be used for single-char or multiple-char keyboard entry

another way to go...
if you want a single key only, you can use something like this...
;*************************************************************************************************************

AnyKey  PROC

;Wait for Any Console Key Press - DednDave 12-2011
;
;  This function returns when any console key is pressed (bKeyDown = 1).
;A possible drawback is that all input event records are removed from
;the console input queue until a key is pressed. In many cases, this is
;not an issue. The virtual key code, virtual scan code, TCHAR character,
;and control key state values are returned in registers.

;Call With: Nothing
;
;  Returns: EAX = TCHAR character (high word of EAX = 0)
;           ECX = control key state flags
;               Bit   Name                Meaning
;                0    RIGHT_ALT_PRESSED   Right ALT key is pressed
;                1    LEFT_ALT_PRESSED    Left ALT key is pressed
;                2    RIGHT_CTRL_PRESSED  Right CTRL key is pressed
;                3    LEFT_CTRL_PRESSED   Left CTRL key is pressed
;                4    SHIFT_PRESSED       SHIFT key is pressed
;                5    NUMLOCK_ON          NUM LOCK light is on
;                6    SCROLLLOCK_ON       SCROLL LOCK light is on
;                7    CAPSLOCK_ON         CAPS LOCK light is on
;                8    ENHANCED_KEY        Key is enhanced
;           EDX:
;           low word (DX) = virtual key code
;               high word = virtual scan code
;
;           all other registers are preserved

        push    ebx
        push    ebp
        sub     esp,(sizeof INPUT_RECORD+3) and -4
        mov     ebp,esp
        INVOKE  GetStdHandle,STD_INPUT_HANDLE
        xchg    eax,ebx
        push    ecx

AnyKy0: INVOKE  ReadConsoleInput,ebx,ebp,1,esp
        movzx   edx,word ptr [ebp].INPUT_RECORD.EventType
        cmp     edx,KEY_EVENT                             ;KEY_EVENT EQU 1
        jnz     AnyKy0

        cmp     edx,[ebp].INPUT_RECORD.KeyEvent.bKeyDown
        jnz     AnyKy0

        pop     ecx
        movzx   eax,word ptr [ebp].INPUT_RECORD.KeyEvent.UnicodeChar
        mov     edx,dword ptr [ebp].INPUT_RECORD.KeyEvent.wVirtualKeyCode
        mov     ecx,[ebp].INPUT_RECORD.KeyEvent.dwControlKeyState
        add     esp,(sizeof INPUT_RECORD+3) and -4
        pop     ebp
        pop     ebx
        ret

AnyKey  ENDP

;*************************************************************************************************************


personally, i prefer the crt method i showed in the earlier post   :P

japheth

Quote from: dedndave on October 21, 2012, 03:58:58 AM
if you want a single key only, you can use something like this...

Looks complicated. What about SetConsoleMode() and resetting ENABLE_LINE_INPUT flag ( temporarily ).

dedndave

it's not that bad - lol
i wrote that function because i wanted the state of the shift/alt/ctrl keys   :P
it's also a synchronous function - which was what i needed for that case

i much prefer the asynchronous method using crt__kbhit and crt__getch as shown in my earlier post
it allows the program to do other things while there is no keystroke
also - the crt functions fix some issues caused by console window bugs that are difficult to deal with

p3tr0va

Quote from: japheth on October 21, 2012, 04:30:16 AM
Looks complicated. What about SetConsoleMode() and resetting ENABLE_LINE_INPUT flag ( temporarily ).

I've already tried that and didn't work.
I'm going to use dedndave's approach, momentarily.
But I still want to know, why the function doesn't work?, what I'm doing wrong?

TouEnMasm

Quote
But I still want to know, why the function doesn't work?, what I'm doing

You just think that a function must run as you want.There is none in the full sdk who can do that.It's the usual mistake of all beginners.
Just use one and add code to adapt it to your need.
Fa is a musical note to play with CL

jj2007

This works:

    push STD_INPUT_HANDLE
    call GetStdHandle   
    mov  hConInput,eax
    push eax
    push edx   ; create a slot
    invoke GetConsoleMode, eax, esp
    pop eax   ; get current mode
    and eax, NOT (ENABLE_LINE_INPUT or ENABLE_ECHO_INPUT)
    pop edx
    invoke SetConsoleMode, edx, eax


Note that MSDN is plain wrong here:

ENABLE_ECHO_INPUT
Characters read by the ReadFile or ReadConsole function are written to the active screen buffer as they are read. This mode can be used only if the ENABLE_LINE_INPUT mode is also enabled.

Right is that setting ENABLE_ECHO_INPUT enables line input, too.

p3tr0va

Quote from: ToutEnMasm on October 22, 2012, 03:16:20 AM
Quote
There is none in the full sdk who can do that.

You're missing something here. Read each one of the post from the beginning.

Thanks jj2007, the problem has been solved. :biggrin:
(but the doubt remains  ::))

dedndave

well - i think your original code would work, with a little change in flow
you do not provide a method for it to catch the carriage return
you might, for example, return 0 in EAX if it is a carriage return or a 1 if otherwise
then, it could be tested in the calling procedure, and not displayed