Author Topic: Win32 Console Addition  (Read 23275 times)

dedndave

  • Member
  • *****
  • Posts: 8828
  • Still using Abacus 2.0
    • DednDave
Re: Win32 Console Addition
« Reply #30 on: April 11, 2013, 06:28:07 AM »
 :biggrin:

long ago, i reported some buggish behaviour of the console window
http://www.masmforum.com/board/index.php?topic=11927.msg90572#msg90572

using the CRT input functions seems to help those problems a little, but not completely
at one time, i debugged into the CRT getch function - it is a very involved routine - lol
the guys at ms have been working on improving the CRT for a long time
so - it's probably as good as it gets

even so, i am always interested in learning more about the specifics of console behaviour
but, if i want something to work in a half-way predictable manner, i write a GUI app   8)

dedndave

  • Member
  • *****
  • Posts: 8828
  • Still using Abacus 2.0
    • DednDave
Re: Win32 Console Addition
« Reply #31 on: April 12, 2013, 11:36:06 PM »
wellllll
i have finally finished analyzing the edit keys - lol

along the way, i discovered a little undocumented fact of line edit modes with ReadConsole and ReadFile
these functions only allow a line length of 254 characters, plus the enter key
so - the simple way to use them is to use a buffer length of 256 chars  :t

still, that doesn't allow you to limit the line length to, say, 10 characters or something

qWord

  • Member
  • *****
  • Posts: 1475
  • The base type of a type is the type itself
    • SmplMath macros
Re: Win32 Console Addition
« Reply #32 on: April 13, 2013, 12:26:02 AM »
Years back I've written the following function, which limits the input by processing the console events retuned by ReadConsoleInput():
Code: [Select]
;__UNICODE__ EQU 1
include \masm32\include\masm32rt.inc
.686

TCCHR macro lbl,args:VARARG
    IFDEF __UNICODE__
        UCCSTR lbl,args
    ELSE
        ?cstr? lbl,args
    ENDIF
endm

ReadConsoleTimeOut proto psz:ptr TCHAR,cc:DWORD,dwTimeOut:DWORD
IsValidTChar proto TChar:DWORD
TStrCopyN proto pDest:ptr TCHAR,ccDest:DWORD,pszSrc:ptr TCHAR,ccSrc:DWORD
TStrCat proto pBuffer:DWORD,ccBuffer:DWORD,psz1:DWORD,psz2:DWORD

.code

main proc
LOCAL sz[256]:TCHAR
   
    print "enter up to 10 characters: "
    invoke ReadConsoleTimeOut,ADDR sz,11,INFINITE
    print "your input: ",%'<'
    print ADDR sz,%'>'
    print chr$(13,10)
   
    print "enter up to 128 characters: "
    invoke ReadConsoleTimeOut,ADDR sz,129,INFINITE
    print ADDR sz,13,10
    print "your input: ",%'<'
    print ADDR sz,%'>'
    print chr$(13,10)

    inkey
    exit
main endp

ReadConsoleTimeOut proc uses edi esi ebx psz:ptr TCHAR,cc:DWORD,dwTimeOut:DWORD
LOCAL hStdIn:HANDLE
LOCAL hStdOut:HANDLE
LOCAL pBuffer:ptr TCHAR
LOCAL nEvents:DWORD
LOCAL ticks:DWORD
LOCAL csbi:CONSOLE_SCREEN_BUFFER_INFO
LOCAL ir[10]:INPUT_RECORD

    mov eax,psz
    mov TCHAR ptr [eax],0
    .if !cc
        ret
    .endif
   
    mov edx,cc
    lea edx,[edx*TCHAR]
    .if !rvx(pBuffer = GlobalAlloc,GPTR,edx)
        ret
    .endif
    mov hStdIn,rv(GetStdHandle,STD_INPUT_HANDLE)
    mov hStdOut,rv(GetStdHandle,STD_OUTPUT_HANDLE)
    .if dwTimeOut != INFINITE
        mov ticks,rv(GetTickCount)
    .endif

    xor edi,edi
    xor esi,esi
    .while 1
        .break .if rv(WaitForSingleObject,hStdIn,dwTimeOut) == WAIT_FAILED || eax == WAIT_TIMEOUT
       
        .if dwTimeOut != INFINITE
            mov edx,rv(GetTickCount)
            sub edx,ticks
            .break .if edx > dwTimeOut
            sub dwTimeOut,edx
            mov ticks,eax
        .endif
       
        lea ebx,ir
        assume ebx: ptr INPUT_RECORD
        .break .if !rv(ReadConsoleInput,hStdIn,ebx,LENGTHOF ir,&nEvents)
        .while nEvents != 0
            .if [ebx].EventType == KEY_EVENT && [ebx].KeyEvent.bKeyDown
                fn GetConsoleScreenBufferInfo,hStdOut,&csbi
                movzx edx,[ebx].KeyEvent.wVirtualKeyCode
                .if edx == VK_RETURN || edx == VK_ESCAPE
                    jmp _exit
                .elseif edx == VK_BACK
            @@: .if edi > 0
                        .if csbi.dwCursorPosition.x > 0
                            sub csbi.dwCursorPosition.x,1
                        .else
                            .if csbi.dwCursorPosition.y > 0
                                sub csbi.dwCursorPosition.y,1
                                movzx eax,csbi.dwSize.x
                                sub eax,1
                                mov csbi.dwCursorPosition.x,ax
                            .endif
                        .endif
                        invoke SetConsoleCursorPosition,hStdOut,DWORD ptr csbi.dwCursorPosition
                        dec edi
                        jmp @F
                    .endif
                .elseif edx == VK_DELETE
            @@: .if edi < esi
                        mov edx,psz
                        lea edx,[edx+edi*TCHAR]
                        mov TCHAR ptr [edx],0
                        lea eax,[edx+TCHAR]
                        invoke TStrCat,psz,cc,psz,eax
                        mov ecx,eax
                        sub ecx,edi
                        invoke WriteConsole,hStdOut,edx,ecx,0,0
                        fn WriteConsole,hStdOut," ",1,0,0
                        invoke SetConsoleCursorPosition,hStdOut,DWORD ptr csbi.dwCursorPosition
                        sub esi,1
                    .endif
                .elseif edx == VK_LEFT
                    .if edi > 0
                        .if csbi.dwCursorPosition.x > 0
                            sub csbi.dwCursorPosition.x,1
                        .else
                            .if csbi.dwCursorPosition.y > 0
                                sub csbi.dwCursorPosition.y,1
                                mov ax,csbi.dwSize.x
                                dec ax
                                mov csbi.dwCursorPosition.x,ax
                            .endif
                        .endif
                        invoke SetConsoleCursorPosition,hStdOut,DWORD ptr csbi.dwCursorPosition
                        sub edi,1
                    .endif
                .elseif edx == VK_RIGHT
                    .if edi < esi
                        movzx eax,csbi.dwSize.x
                        sub eax,1
                        .if csbi.dwCursorPosition.x >= ax
                            mov csbi.dwCursorPosition.x,0
                            add csbi.dwCursorPosition.y,1
                        .else
                            add csbi.dwCursorPosition.x,1
                        .endif
                        invoke SetConsoleCursorPosition,hStdOut,DWORD ptr csbi.dwCursorPosition
                        add edi,1
                    .endif
                .else
                    lea edx,[esi+1]
                    movzx ecx,[ebx].KeyEvent.UnicodeChar
                    .if edx < cc && rv(IsValidTChar,ecx)
                        mov edx,psz
                        lea eax,[edx+edi*TCHAR]
                        invoke TStrCopyN,pBuffer,cc,eax,-1
                       
                        movzx ecx,TCHAR ptr [ebx].KeyEvent.UnicodeChar
                        mov WORD ptr [edx+edi*TCHAR],cx
                        mov WORD ptr [edx+edi*TCHAR+TCHAR],0
                        mov esi,rv(TStrCat,psz,cc,psz,pBuffer)
                        sub eax,edi
                        lea edx,[edx+edi*TCHAR]
                        invoke WriteConsole,hStdOut,edx,eax,0,0
                        add edi,1
                       
                        movzx edx,csbi.dwSize.x
                        sub edx,1
                        .if csbi.dwCursorPosition.x >= dx
                            mov csbi.dwCursorPosition.x,0
                            add csbi.dwCursorPosition.y,1
                        .else
                            add csbi.dwCursorPosition.x,1
                        .endif
                        invoke SetConsoleCursorPosition,hStdOut,DWORD ptr csbi.dwCursorPosition
                    .endif
                .endif
            .endif
            add ebx,INPUT_RECORD
            dec nEvents
        .endw
    .endw

_exit:
    invoke GlobalFree,pBuffer
    fn WriteConsole,hStdOut,chr$(13,10),2,0,0
   
    mov eax,esi
    ret

ReadConsoleTimeOut endp

; OUT: valid if eax > 0
IsValidTChar proc uses edx ecx TChar:DWORD

    .const
        TCCHR ValidCharT," \t.,:;\x-_#'\q+-*/\\?{}\a\b[]&\p$\r\l|^~ยด`=@"
    .code
   
    .if rv(IsCharAlphaNumeric,TChar)
        ret
    .endif
    and TChar,0ffffh
    mov edx,OFFSET ValidCharT
    movzx eax,TCHAR ptr [edx]
    .while eax != 0
        lea edx,[edx+TCHAR]
        .break .if eax == TChar
        movzx eax,TCHAR ptr [edx]
    .endw
    ret

IsValidTChar endp

TStrCopyN proc uses ecx edx ebx edi esi pDest:ptr TCHAR,ccDest:DWORD,pszSrc:ptr TCHAR,ccSrc:DWORD
LOCAL cc:DWORD

    mov edx,pDest
    mov ecx,ccDest
    mov eax,pszSrc
    mov cc,0
    .repeat
        sub ecx,1 ; for termination zero
        .break .if ZERO?
        test ccSrc,-1
        .break .if ZERO?
        .repeat
            movzx ebx,TCHAR ptr [eax]
            test ebx,ebx
            .break .if ZERO?
            IFDEF __UNICODE__
                mov TCHAR ptr [edx],bx
            ELSE
                mov TCHAR ptr [edx],bl
            ENDIF
            lea eax,[eax+TCHAR]
            lea edx,[edx+TCHAR]
            add cc,1
            sub ccSrc,1
            .break .if ZERO?
            sub ecx,1
        .until ZERO?
    .until 1
    mov TCHAR ptr [edx],0
    mov eax,cc
    ret
   
TStrCopyN endp

TStrCat proc uses ecx edx ebx edi esi pBuffer:DWORD,ccBuffer:DWORD,psz1:DWORD,psz2:DWORD
   
    mov edi,pBuffer
    mov ecx,ccBuffer
    mov esi,psz1
    mov edx,psz2
    mov eax,1
    mov ccBuffer,0
   
    sub ecx,1 ; for termination-zero
    jz _exit
   
_begin:
    movzx ebx,TCHAR ptr [esi]
    test ebx,ebx
    jz @F
    IFDEF __UNICODE__
        mov TCHAR ptr [edi],bx
    ELSE
        mov TCHAR ptr [edi],bl
    ENDIF
    lea edi,[edi+TCHAR]
    lea esi,[esi+TCHAR]
    inc ccBuffer
    sub ecx,1
    jz _exit
    jmp _begin
@@:
    test eax,eax
    cmovnz eax,ebx
    cmovnz esi,edx
    jnz _begin
_exit:
    mov TCHAR ptr [edi],0
   
    mov eax,ccBuffer
    ret

TStrCat endp
end main
MREAL macros - when you need floating point arithmetic while assembling!

dedndave

  • Member
  • *****
  • Posts: 8828
  • Still using Abacus 2.0
    • DednDave
Re: Win32 Console Addition
« Reply #33 on: April 13, 2013, 12:44:12 AM »
interesting code, qWord
i have something similar in mind, but i had no intention of providing a timeout - lol

the basic form i am using looks like this...
Code: [Select]
    .repeat
        .repeat
            INVOKE  ReadConsoleInput,hStdInp,addr ir,1,addr uRecCnt
            movzx   edx,word ptr ir.EventType
        .until (edx==KEY_EVENT) && (edx==ir.KeyEvent.bKeyDown)
        movzx   eax,word ptr ir.KeyEvent.UnicodeChar
        mov     ecx,ir.KeyEvent.dwControlKeyState
        movzx   edx,word ptr ir.KeyEvent.wVirtualKeyCode

;       .if
;       .elseif
;       .elseif                     handle line input and edit keys here
;       .elseif
;       .endif

    .until (ax==VK_RETURN) && !(cx&(LEFT_CTRL_PRESSED or RIGHT_CTRL_PRESSED or LEFT_ALT_PRESSED or RIGHT_ALT_PRESSED))

qWord

  • Member
  • *****
  • Posts: 1475
  • The base type of a type is the type itself
    • SmplMath macros
Re: Win32 Console Addition
« Reply #34 on: April 13, 2013, 01:53:08 AM »
but i had no intention of providing a timeout
removing that few extra lines should be no problem for anyone  ;)
MREAL macros - when you need floating point arithmetic while assembling!

dedndave

  • Member
  • *****
  • Posts: 8828
  • Still using Abacus 2.0
    • DednDave
Re: Win32 Console Addition
« Reply #35 on: April 13, 2013, 02:28:48 AM »
you know me - i like to write my own stuff   :P
i am not much of a script kiddie

here are some operations i found - couple of them i wasn't aware of

line edit supports these keys:
backspace, tab, enter, insert, delete, home, end, left/right cursor

combinations:
ctrl left/right arrow - moves cursor to first/last character
ctrl home - removes all characters preceeding the cursor
ctrl end - removes all characters from cursor to end of line

i didn't play with all the key combinations - there might be an "undo" somewhere   :P
or - there could be

jj2007

  • Member
  • *****
  • Posts: 12949
  • Assembler is fun ;-)
    • MasmBasic
Re: Win32 Console Addition
« Reply #36 on: April 13, 2013, 03:13:55 AM »
i didn't play with all the key combinations - there might be an "undo" somewhere   :P
or - there could be

Ctrl Z doesn't work, and SetConsoleCtrlHandler concerns only special keys like Ctrl C and Ctrl Break. If you are really keen on undo, handle Ctrl Break...

dedndave

  • Member
  • *****
  • Posts: 8828
  • Still using Abacus 2.0
    • DednDave
Re: Win32 Console Addition
« Reply #37 on: April 13, 2013, 03:33:26 AM »
ctrl-z is normally used to generate an EOF (SUB char)

ctrl-c and ctrl-break should exit/abort
i can make that happen, if i want to
but, my thinking was that the guy might have some ctrl handler in place to handle log off or shut down

i doubt there is an undo in the console - lol
but, we could add our own
i was thinking maybe one of the function keys, perhaps with ctrl or alt

dedndave

  • Member
  • *****
  • Posts: 8828
  • Still using Abacus 2.0
    • DednDave
Re: Win32 Console Addition
« Reply #38 on: April 13, 2013, 07:53:59 AM »
in this first set of routines, i use a ConIn256 routine that assumes a buffer length of 256 TCHAR's
that makes it simple
in the attachment are both ANSI and UNICODE builds

any problem reports appreciated   :P