News:

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

Console and keyboard input

Started by Xpander, August 01, 2006, 12:38:18 PM

Previous topic - Next topic

Xpander

I am trying to learn Windows assembly using MASM32 and have acquired a couple of books. Previously I have coded some assembly on other CPUs.
I am a bit confused about keyboard input, and the more I study my books and google, the more confused I get.

What I want to achieve is to simply code a program that runs in a Windows XP command prompt and wait for one character to be pressed - e.g. Esc - and then exit! It should not open any windows or so - and YES - I have seen the Iczelion tutorials, and the keyboard input sample seem to bloated for me at the moment - besides I want this to run as a console application, not in a window.

Do I need to use int 16h? Afaik, it cannot be used under Windows..

It sounds so extremely simple, and I would be very grateful if someone could explain how to do it before I tear what's left of my hair out... :-( Preferably I'd like to see a sample..

hutch--

Xpander,

Welcome on board. Console IO in Windows is different in that you must use system functions to do either rather than low level interrupts but its no big deal to do once you get the swing of it. have a look at the masm macros in masm32 and then have a look at the library procedures that they call in the masm32 library. You can access the full source of StdOut and StdIn so its not a problem to see how it works.

The real fun in 32 bit assembler is when you process data as it can be done very quickly if you know what you are doing.
Download site for MASM32      New MASM Forum
https://masm32.com          https://masm32.com/board/index.php

japheth


Hi,

the functions you should have a look at are

- GetStdHandle()
- SetConsoleMode()
- ReadConsoleInput()

dos int 21h, ah=08h is admittedly more simple

Xpander

Thank you both for your replies.

I just read the reply from hutch and tried some stuff. I will need to look more into the functions that japheth suggested.

Anyway, this is what I've come up with and it's not working:


;test2.asm
.386
.model flat, stdcall
option casemap :none   ; case sensitive

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib kernel32.lib

STD_INPUT equ -10

.data
chrin      dword      ?

.code
_start:

_wait:
invoke ReadFile,STD_INPUT,NEAR32 PTR chrin,1,0,NULL

mov eax, chrin
cmp eax, 01bh
jne _wait

invoke ExitProcess,0

end _start


I use the following makefile:


NAME=test2
$(NAME).exe: $(NAME).obj
        link /SUBSYSTEM:CONSOLE /LIBPATH:c:\masm32\lib $(NAME).obj
$(NAME).obj: $(NAME).asm
        ml /c /coff /Cp $(NAME).asm


I can do a similar routine using STD_OUTPUT and WriteFile and output a char. That works fine. What I am trying to do in the code above, is to wait for Esc to be pushed (which I think should be $1B), and then exit. The result I am getting so far, is that the keys I push are output to screen, and that's it. It doesn't exit when I hit Esc. I've also tried comparing for other key codes, but same thing.

I guess SetConsoleMode might be the clue to avoid the keys I push to be displayed (I'd like to avoid that), but I still don't understand why my program doesn't exit on Esc. Anyone? I will keep hunting on my own meanwhile..

I appreciate any help :-)

Xpander

OK. So I modified my code a bit, trying to use ReadConsoleInput(). I still fail :-(

Anyone care to explain why I am still failing, as I feel very, very lost..


.386
.model flat, stdcall
option casemap :none   ; case sensitive

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib kernel32.lib

STD_INPUT_HANDLE equ -10

.data
hStdIn dword 0
chrin dword ?

.code
_start:

invoke GetStdHandle, STD_INPUT_HANDLE
mov hStdIn, eax

invoke FlushConsoleInputBuffer, hStdIn
invoke SetConsoleMode, hStdIn, ENABLE_LINE_INPUT or ENABLE_ECHO_INPUT or ENABLE_PROCESSED_INPUT

_wait:

invoke ReadConsoleInput, hStdIn, NEAR32 PTR chrin, 1, 1
mov eax, chrin
cmp eax, 01bh
jne _wait

invoke ExitProcess,0

end _start


MichaelW

Hi Xpander,

I would use a simpler method, but if you are going to use ReadConsoleInput then you must set up the necessary data and call the function correctly.

MSDN: ReadConsoleInput

Your code must pass the address of an array of INPUT_RECORD structures and the address of a DWORD to receive the number of input records read. For reading a single key event the array can consist of a single INPUT_RECORD structure, defined in your data as something like this:

ir INPUT_RECORD <>

So the invoke would be something like this

invoke ReadConsoleInput, hStdIn, ADDR ir, 1, ADDR nEvents

And you would test for the escape key with a statement like this:

cmp ir.KeyEvent.wVirtualKeyCode, 1bh

But there is an unfortunate complication that you need to deal with. The INPUT_RECORD structure in the MASM32 windows.inc has an error that will misalign the UNION, and prevent you from detecting the key. To correct the problem, define the correct structure in your code, using an altered name so it will not conflict with the definition in windows.inc:

_INPUT_RECORD STRUCT
        EventType   WORD ?
        WORD ?                    ; For alignment
        UNION
            KeyEvent              KEY_EVENT_RECORD          <>
            MouseEvent            MOUSE_EVENT_RECORD        <>
            WindowBufferSizeEvent WINDOW_BUFFER_SIZE_RECORD <>
            MenuEvent             MENU_EVENT_RECORD         <>
            FocusEvent            FOCUS_EVENT_RECORD        <>
          ENDS
_INPUT_RECORD ENDS


And change the data definition to

ir _INPUT_RECORD <>

eschew obfuscation

Xpander

Many thanks to all of you for responding!

The post from MichaelW did the trick for me. Somehow I managed to get it partially working with the 'buggy' INPUT_RECORD - but I knew something was very wrong, because if I did hit another key than Esc, the CPU spiked to 100% CPU. Sometimes it would spike the CPU right away, and ignore the Esc. Obviously that bug aped things up for me :-( Thanks alot for that post, after reading it I modified my code and things finally worked!

MichaelW - You also mentioned you'd prefer a simpler method. Would you mind explaining that a bit further?

I am including my working result here:


.386
.model flat, stdcall
option casemap :none   ; case sensitive

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
includelib kernel32.lib

STD_INPUT_HANDLE equ -10

.data

evEvents word ?
evBuffer _INPUT_RECORD <>
hStdIn dword 0

.code
_start:
invoke GetStdHandle, STD_INPUT_HANDLE
mov hStdIn, eax

invoke SetConsoleMode, hStdIn, ENABLE_PROCESSED_INPUT

_wait:
invoke ReadConsoleInput, hStdIn, ADDR evBuffer, 1, ADDR evEvents
test evEvents, 0ffffh
jz _wait

mov ax, evBuffer.KeyEvent.wVirtualKeyCode
cmp ax, 01bh
jne _wait

invoke ExitProcess,0
end _start

MichaelW

I would just use the MASM32 inkey macro and wait for any key, but if you must wait for Escape, then perhaps something like this:

@@:
    invoke crt__getch
    cmp eax, 1bh
    jne @B


Any method that does not first flush the input buffer will not wait if the buffer contains the target key when the method is called.
eschew obfuscation

GregL

Xpander,

Just a note, evEvents should be a DWORD instead of a WORD.  :bg

Xpander

MichaelW, thanks again. I wasn't aware of that one. I will give that a try aswell :-)

Greg, Oops :-) Thanks for pointing that out! I have corrected my code.