News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

Console string input function?

Started by Brannagan, September 04, 2014, 06:43:08 PM

Previous topic - Next topic

Brannagan

Does MASM32 include a Console string input function which can do the following:

1. Allows me to specify a maximum input length of the string

2. Handles input editing keys, such as arrow keys, backspace, and delete?


jj2007

include \masm32\MasmBasic\MasmBasic.inc      ; download
  Init
  Let esi=Left$(Input$("Type something and hit Enter: ", "This string is longer than ten chars"), 10)
  Inkey "You typed [", esi, "]"
  Exit
end start

hutch--

Have a look at the MASM32 "StdIn" library function.

StdIn proc lpszBuffer:DWORD,bLen:DWORD

dedndave

here is one i wrote that is UNICODE aware
it's a little fancier than the one in the masm32 package

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

ConIn256 PROC USES EBX ESI EDI lpBuffer:LPVOID

;UNICODE aware ConIn256 routine - DednDave, 4-2013
;the buffer length must be at least 256 TCHAR's
;the function allows up to 254 characters to be entered

;--------------------------------------------

    LOCAL   dwOrigModeOut         :DWORD
    LOCAL   dwOrigModeInp         :DWORD
    LOCAL   nNumberOfCharsRead    :DWORD

;--------------------------------------------

    INVOKE  GetStdHandle,STD_OUTPUT_HANDLE
    xchg    eax,esi                                ;ESI = hStdOut
    INVOKE  GetStdHandle,STD_INPUT_HANDLE
    xchg    eax,ebx                                ;EBX = hStdInp
    INVOKE  GetConsoleMode,esi,addr dwOrigModeOut
    INVOKE  GetConsoleMode,ebx,addr dwOrigModeInp
    mov     edx,ENABLE_PROCESSED_OUTPUT or ENABLE_WRAP_AT_EOL_OUTPUT
    .if edx!=dwOrigModeOut
        INVOKE  SetConsoleMode,esi,edx
    .endif
    mov     edx,ENABLE_ECHO_INPUT or ENABLE_LINE_INPUT or ENABLE_PROCESSED_INPUT
    .if edx!=dwOrigModeInp
        INVOKE  SetConsoleMode,ebx,edx
    .endif
    xor     ecx,ecx
    mov     edi,lpBuffer
    mov     nNumberOfCharsRead,ecx
    INVOKE  ReadConsole,ebx,edi,256,addr nNumberOfCharsRead,ecx
    mov     edx,dwOrigModeOut
    .if edx!=(ENABLE_PROCESSED_OUTPUT or ENABLE_WRAP_AT_EOL_OUTPUT)
        INVOKE  SetConsoleMode,esi,edx
    .endif
    mov     edx,dwOrigModeInp
    .if edx!=(ENABLE_ECHO_INPUT or ENABLE_LINE_INPUT or ENABLE_PROCESSED_INPUT)
        INVOKE  SetConsoleMode,ebx,edx
    .endif
    mov     eax,nNumberOfCharsRead
    sub     eax,2
    mov TCHAR ptr [edi+TCHAR*eax],0
    ret

ConIn256 ENDP

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


the ReadConsole function always wants a 256 char buffer
i couldn't really find a simple way to limit the length of the win32 console input buffer to a specific size
not like the old DOS functions, at all
you could read individual chars, but then you'd have to handle line-editing, as well

attached is a little test program (can be assembled as either UNICODE or ANSI)

jj2007

Quote from: dedndave on September 04, 2014, 09:21:01 PM
here is one i wrote that is UNICODE aware

Unicode is always challenging ;-)

Attached an example. Note that Russian should always work, but the Chinese and Arabic stuff might not show correctly on some systems.

Choose a language: 1=English, 2=Russian, 3=Arabic, 4=Chinese, Escape=exit
Edit the text, or hit Enter to confirm: Введите текст здесь


(the *.asc source is RTF and contains the rc file; if opened in RichMasm, hit F6 to build it)

Brannagan

Thanks to everyone for the replies.  It appears that all these solutions allow the user to type more then the maximum allowed number of characters into the input line,  and so as dedndave mentioned, I might have to create my own.

jj2007

In case you are worried about buffer overflows, test it:

include \masm32\include\masm32rt.inc

.data
buffer   db "This is a buffer"
   db 0
afterbuffer   db "Still intact?", 0

.code
start:   print "["
   invoke StdIn, addr buffer, sizeof buffer
   print offset buffer, "]", 13, 10
   inkey offset afterbuffer
   exit

end start


In contrast, MasmBasic Input$() does return a longer string, but the limit is 160,000 bytes, which is way beyond what the console buffer would allow.

So both solutions are safe.

Brannagan

Thanks JJ,

The issue I have is purely cosmetic, as my Console application is formatted with line-drawn boxes where the input strings are accepted, and I wanted to prevent more then xx characters from being typed so that the input does not exceed these boxes, and continue on the next line(s). It appears all these input fuctions allow up to 254 characters to be typed in, and then they simply ignore the extra ones. I've written a character by character input routine, but adding support for arrows, delete, and inserting characters in the middle turns what should have been a simple task into something more.

dedndave

if that's the case, i suggest using ReadConsoleInput

http://msdn.microsoft.com/en-us/library/windows/desktop/ms684961%28v=vs.85%29.aspx

in my previous example, i use a routine named AnyKey, which will show you how to remove a single character from the buffer using ReadConsoleInput
you could use that routine as a guideline

it shouldn't be hard to add line edit (backspace, left/right arrow, insert/delete modes)

jj2007

Quote from: Brannagan on September 05, 2014, 04:20:56 AM
Thanks JJ,

The issue I have is purely cosmetic, as my Console application is formatted with line-drawn boxes where the input strings are accepted, and I wanted to prevent more then xx characters from being typed

I see. That is indeed difficult. Input$() uses

   invoke SetConsoleMode, eax, ENABLE_LINE_INPUT or ENABLE_ECHO_INPUT or ENABLE_PROCESSED_INPUT
   invoke ReadFile, hInput, pBuffer, bLen, ADDR bRead, NULL

... but even if you set bLen=10, the user can type many more characters. Must be a Windows feature ::)

Vortex

Hi Brannagan,

Here is another example :

include        fgets.inc

BUFF_SIZE equ 16

.data

f1      db '%s',0

.data?

stdin   dd ?
buffer  db BUFF_SIZE dup(?)

.code

start:

    call    crt___p__iob
    mov     stdin,eax           ; #define stdin  (&__iob_func()[0])

    invoke  crt_fgets,ADDR buffer,BUFF_SIZE,stdin

    invoke  crt_printf,ADDR f1,ADDR buffer
           
    invoke  ExitProcess,0

END start


QuoteRemarks

The fgets function reads a string from the input stream argument and stores it in str. fgets reads characters from the current stream position to and including the first newline character, to the end of the stream, or until the number of characters read is equal to n – 1, whichever comes first. The result stored in str is appended with a null character. The newline character, if read, is included in the string.

http://msdn.microsoft.com/en-us/library/c37dh6kf.aspx

Brannagan

Hi Vortex,

Thank you for posting this, but unfortunately it has the same cosmetic issue as all the other suggestions. Even though your example has "BUFF_SIZE EQU 16", it allows more then 16 characters to be typed in. This one appears to allow an endless number if characters to be typed, as I filled my entire console screen with input characters when I ran your exe.

My solution was to roll my own. To keep it simple I just display standard characters, and when the backspace key is pressed, I print backspace,space,backspace to eat the previous character (so no cursor positioning code was needed) and decrement my character counter.
If I add support for arrow keys and the delete/insert keys later it will become a bit more complex of course.

Vortex

Hi Brannagan,

You are right. fgets can only truncate the input string to the buffer size.