I am trying to find a way to wait for a key to be pressed from a Console, and detmine the scancode of they key that was pressed.
I need the scan code so I can redirect the I/O to a DOS application.
In DOS I can do it using the BIOS like this:
mov ah,0
INT 16h
Which returns:
AH = BIOS scan code
AL = ASCII character
I've tried the GETKEY macro, as well as looked at the MSVCRT functions, but I can not find a way to do it in Windows.
Have I overlooked something?
Mike
Mike,
What OS do you intend to use here, a true 16 bit MS-DOS, a CMD.EXE window in Windows and is the application you have in mind a 16 bit DOS app or a 32 bit PE executable ?
you can use crt__kbhit to poll the keyboard
if there is a key present....
you will want to get it from the buffer with crt_getch
for extended or function keys, you need to call crt_getch a second time
on the first call, AL will either be 0E0h for extended keys or 0 for function keys
if AL is any other value, it is a normal key and only one call is needed
this is a function i wrote that polls the keyboard, but returns immediately if no key is present
;--------------------------------------------------------------------------
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: ret
InKyb ENDP
;--------------------------------------------------------------------------
in the masm32 library, there is a wait_key function that waits for a keypress
but, it does not handle extended or function keys correctly
you can use the above routine in a loop with Sleep....
@@: call InKyb
jnz @F
INVOKE Sleep,50
jmp @B
@@:
50 mS is a good wait period - the smallest i would use would be 40 mS
Hutch - My app is a Win32 Console app.
DednDave - Thanks.. I'm going to give your code a try. :-)
Mike
DednDave - That was very helpful but it lacks providing the scan code for every character...
For example, Here are some codes which DOS provides:
When I press 'a' Scan Code= 1Eh Ascii Code = 61h
When I press 'b' Scan Code= 30h Ascii Code = 62h
When I press 'c' Scan Code= 2Eh Ascii Code = 63h
I was hoping there is a Win32 API which can provide me with the same scan codes, to save me the trouble
of having to manually create a table with the scan codes for every key (plus various CTRL-ALT-SHIFT states).
Any ideas?
Mike
ah, yes - sorry i missed that in your post
for that, you will want to use ReadConsoleInput
not as nice, because the crt takes care of some bugs with the CMD window
but, give this a try....
;*************************************************************************************************************
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
;*************************************************************************************************************
no loop needed for that one, as it waits for a keypress
Thanks so much DednDave, that works perfect! :greenclp:
See if this helps.
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
print "Hi, I am waiting for you to press the 'q' key",13,10
back:
invoke GetAsyncKeyState,VK_Q
test eax, eax
jz back
print hex$(eax),13,10
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
i guess he wants the scan code, Hutch
but, that is a simple snippet :t
@ DednDave
Excellent code. That cleared two annoying problems.
Thank you.
Dave,
What I like about GetAsyncKeyState is you can gang up any key combinations and this makes it very flexible. It is also effectively free of the irritations of the console API functions.
many forum members don't like the way i use the stack
and, they prefer indented .if/.endif and so on
this is a cleaned up version, but may be a few bytes larger
it's probably easier to understand
;*************************************************************************************************************
AnyKey PROC
;Wait for Any Console Key Press - DednDave
;version 1, 12-2011
;version 2, 2-2013
;
; 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
;-------------------------------------------------
LOCAL ir :INPUT_RECORD
LOCAL uRecCnt :UINT
LOCAL hStdInp :HANDLE
;-------------------------------------------------
INVOKE GetStdHandle,STD_INPUT_HANDLE
mov hStdInp,eax
.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
mov edx,dword ptr ir.KeyEvent.wVirtualKeyCode
ret
AnyKey ENDP
;*************************************************************************************************************
the only time you might have a problem with this routine is if mouse input is to be used while waiting for a key
that seems rather rare
in some cases, it may actually be desirable to flush the mouse input events
Hutch,
yah - i may play with that code when i have some time
i believe that crt__getch uses ReadConsoleInput, and i like the way it behaves - lol
that's why i used it, but that doesn't mean it's the best way :P :P
Dave, you may like this one, its one of the reasons why I use GetAsyncKeyState().
IF 0 ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
Build this template with "CONSOLE ASSEMBLE AND LINK"
ENDIF ; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
include \masm32\include\masm32rt.inc
.code
start:
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
call main
inkey
exit
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
main proc
print "Press the keys 'd' 'a' 'v' 'e' together to exit",13,10
lpst:
invoke GetAsyncKeyState,VK_D
test eax, eax
je lpst
invoke GetAsyncKeyState,VK_A
test eax, eax
je lpst
invoke GetAsyncKeyState,VK_V
test eax, eax
je lpst
invoke GetAsyncKeyState,VK_E
test eax, eax
je lpst
print "TADA",13,10
ret
main endp
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
end start
:biggrin:
here, lemme put that in a string and use LODSB :lol:
Dave - The new version looks much cleaner, thanks!
i
Hutch - The GetAsyncKeyState() looks intresting, I'm sure I'll have uses for that in the future.
Mike
it's essentially the same code, just written in a different style
Hi Dave,
I found a little bug in the AnyKey procedure. If the user presses Ctrl-C, it will shut down my application.
Is there any way to prevent this from happening? Otherwise I may end up needing to create a scan table
and stick with GetKey. :-(
Mike
i have noticed some "peculiarities" in the routine, also
for example, when pressing keys like shift, control, alt, the routine exits with a keystroke
it is somewhat debatable if that is desirable - lol
and - another debate for caps lock, scroll lock, num lock, etc
these issues can be handled by simply adding filters to the code, and looping back
let me know if you want an example of how to do this
as for the specific problem of Ctrl-C - it's not really a bug - it's considered normal :biggrin:
you could use SetConsoleCtrlHandler during program initialization to alter this behaviour
however, i don't think you can avoid the Ctrl-Break combination
http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx (http://msdn.microsoft.com/en-us/library/windows/desktop/ms686016%28v=vs.85%29.aspx)
INVOKE SetConsoleCtrlHandler,NULL,TRUE
ok - i was wrong..... again :biggrin:
you can capture Ctrl-Break by writing a handler routine
in the attached test program, i wrote a simple handler that intercepts both Ctrl-C and Ctrl-Break
i also added filters to AnyKey so that it does not exit for shift, control, or alt keys
now, the problem is that no keystroke is generated for Ctrl-C
i may be able to insert that stroke into the stream manually
let me try it on the next go
note: there are numerous Ctrl-? strokes that do not place anything into the stream (Ctrl-S, for example)
Thanks Dave that did the trick for Ctrl-C. I'm not too concerned about Ctrl-Break as that seems to kill most Console programs, so I can live with that. I did have to add filters to ignore the plain ALT, CTL and SHIFT key results... but since it's named AnyKey it lives up to it's name. :)
Mike
yah - my initial use for this routine was simply "wait for any key to be pressed"
i decided to add the extra functionality because the info was there :P
Your new test version works very well so far. I don't need to test for Ctrl-C in my app so I may use this version instead. :-)
Mike
ok - well, if you need to, you can use WriteConsoleInput to insert INPUT_RECORD structures into the stream
for each keypress, you would want a record for key down and a record for key up