Page 1 of 2

Attempt to catch arrow keystrokes

Posted: 12 May 2014 16:15
by aGerman
Wouldn't it be awesome to work with arrow keys in a Batch file?

As we know PAUSE is waiting for user input and immediately returns on any keystroke. Recently I stumble on a side effect. Arrow keys (as well as function keys that are not assotiated with ASCII characters) seem to send two characters to PAUSE where one PAUSE command only processes one character.

Code: Select all

@echo off &setlocal
set "n=0"
:loop
set /a "n+=1"
pause>nul
echo %n%
goto loop

As you can see such keys let the loop iterate twice. Not realy surprising to me since I know that behavior from C programming. E.g. the (nonstandard) getch function has to be called twice to get the character read from an arrow key where the first call returns 0.

Having this in mind I tried to let one PAUSE>NUL eat the 0 and preserve the second character read for another command.
What are the choices? I vainly tried SET /P. The XCOPY technique reads funny things out from the process memory ...

Code: Select all

@echo off &setlocal
for /f "delims=" %%i in ('pause^>nul^|xcopy /lwq "%~fs0" :\') do echo "%%i"
pause

... but all it reads is not reasonable to distinguish between the keys that where pressed (and has probably nothing to do if you pressed an arrow key or any other).

Is there anything else we could try or is it just a hopeless task?

Regards
aGerman

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 03:38
by jeb
Long time ago I tried this too, without any success :(

The only thing I know is that arrow keys produces a two character sequence.

A pause can read these characters, but copy, xcopy, set/p nor more consumes the second character.

So there seems to exist two character buffers.

Code: Select all

@echo off

echo Press an arrow key
pause

set /p=Enter anything, even use arrow keys

echo The next pause consumes the second part of the arrow key
pause


This works even for multiple normal keys, even newly entered arrow keys on the console or xcopy, the second part is still stored until the next pause command.

I never found another command that can even consume this.

jeb

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 11:44
by einstein1969
Hi,

yesterday I have tried and i have catch the double buffer existence.

I have seen that the buffer in a cmd line session survive over and over until next pause command

Code: Select all

>pause
Premere un tasto per continuare . . .

> any command ?

>pause
Premere un tasto per continuare . . .


If you press an arrow key the next pause get the next key will not pause and flush the input buffers

I have found that the command "edlin" can(EDIT:could) have a influence on this.

On my windows 7 the cpu go 100%

but not remember what i do :evil: :( :? :oops: :roll:

EDIT: is possible try with the doskey disabled?

Edit2: the edlin catch the keypressed(arrow key) but i dont know if it is the solution

Code: Select all

Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? H
Abort edit (Y/N)?
Abort edit (Y/N)? M
Abort edit (Y/N)?
Abort edit (Y/N)? M
Abort edit (Y/N)?
Abort edit (Y/N)? M
Abort edit (Y/N)?
Abort edit (Y/N)? K
Abort edit (Y/N)?
Abort edit (Y/N)? K
Abort edit (Y/N)?
Abort edit (Y/N)? K
Abort edit (Y/N)?
Abort edit (Y/N)? H



einstein1969

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 12:51
by aGerman
EDIT: is possible try with the doskey disabled?

Edit2: the edlin catch the keypressed(arrow key) but i dont know if it is the solution

Ha :?: Could you explain what exactly you did? I never used EDLIN before.

Regards
aGerman

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 13:09
by Squashman
EDLIN not available on 64 bit Windows.

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 13:22
by einstein1969
aGerman wrote:
EDIT: is possible try with the doskey disabled?

Edit2: the edlin catch the keypressed(arrow key) but i dont know if it is the solution

Ha :?: Could you explain what exactly you did? I never used EDLIN before.

Regards
aGerman


Every time I go to 100% cpu I have to close the cmd and I lose my job. I have strong headache and nause is that I do not remember what I'm doing :(

However now I tried again:

flush the pause buffer with pause>nul than when empty do:

from the cmd line run:

Code: Select all

pause> nul 


Press an arrow key. This will fill the second/pause buffer with two key/char and one is used

than from cmd line run:

Code: Select all

edlin ppp 


than write "quit" and ENTER

appears

Code: Select all

  Abort edit (Y/N)? 


then the cpu not go 100%

and if you press the arrow key you get this:

Code: Select all

Abort edit (Y / N)? H
Abort edit (Y / N)?
Abort edit (Y / N)? K
....


a little cumbersome but it 's the best I could do

@squashman: Thanks!

einstein1969

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 13:23
by Squashman
You could always use Carlos' BG.EXE utility.
viewtopic.php?f=3&t=4094

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 13:33
by einstein1969
Squashman wrote:You could always use Carlos' BG.EXE utility.


Yes I know. But we are looking for a method that uses native commands as much as possible.

Too bad that "edlin" is not present in 64bit systems :(

@All
However, does anyone know how to disable the doskey?

I think there is a possibility ....

einstein1969

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 15:52
by aGerman
The EDLIN technique is quite cool but I'm not sure if we could process its output in a reasonable manner. Something like passing Q via pipeline lets EDLIN freeze. I realy don't know how to automate it.

In the mean time I also fiddled with CHOICE which also seems to react on the second byte that a previous PAUSE command left (as the beep lets assume). But none of the supported alphanumeric characters are working.

However, does anyone know how to disable the doskey?

You can delete DOSKEY macros but I don't know how to disable this command. What behavior do you expect when disabling it?

Regards
aGerman

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 16:30
by einstein1969
aGerman wrote:The EDLIN technique is quite cool but I'm not sure if we could process its output in a reasonable manner. Something like passing Q via pipeline lets EDLIN freeze. I realy don't know how to automate it.

In the mean time I also fiddled with CHOICE which also seems to react on the second byte that a previous PAUSE command left (as the beep lets assume). But none of the supported alphanumeric characters are working.

However, does anyone know how to disable the doskey?

You can delete DOSKEY macros but I don't know how to disable this command. What behavior do you expect when disabling it?

Regards
aGerman


If you are in a set /p piece of code and then you press the arrow key the doskey is present.
I want to observe what happens when there isn't doskey in memory.

einstein1969

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 16:52
by einstein1969
try this

Code: Select all

start /B cmd /F:off /q /c " (for /l %i in (1,1,50) do (ping -n 2 127.0.0.1>nul & echo quit &echo N)) | edlin ppp"


and press arrow key and/or BACKSPACE

einstein1969

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 17:27
by einstein1969
this seem flush the pause buffer:

Code: Select all

dir /P


einstein1969

Re: Attempt to catch arrow keystrokes

Posted: 13 May 2014 22:27
by GuruGary
The reason for the "double buffer" is because the arrow keys are "extended" keys. When you press a key on the keyboard, it sends a "scan code" which is a single-byte value (0-255). When they added additional keys to the keyboard, there were more than 255 values that could be sent. The way they worked around that was to make the new non-ASCII keys "extended" keys. So an extended key sends a two single-byte values where the first byte sent is 0x00. So from a programming standpoint, when you are reading keyboard keys and the scan-code is 0x00, then you have to read it a 2nd time since it is an extended key.

None of that really solves your problem other than to know why it happens. The arrow keys, F keys (F1, F2, etc.), Home, Pg Up, and most of your Alt+Key combinations are all extended keys and will give the same "double buffer" issue that you mentioned. My suggestions would be:
* Use the Set /p command and ask the user to press Enter (set /p key=Press Enter to continue)
* Use an external program to look for a keypress instead of pause, like getkey (this does not come with DOS/Windows and would need to be downloaded or installed)
* Use an external program to clear the keyboard buffer immediately after the pause command (this does not come with DOS/Windows and would need to be downloaded or installed)

Good luck!

Re: Attempt to catch arrow keystrokes

Posted: 14 May 2014 00:27
by Liviu
aGerman wrote:Wouldn't it be awesome to work with arrow keys in a Batch file?

Good question. Problem is that most command line utilities work at the "standard streams" interfaces, and rightfully so since that's what allows the usual redirection and piping. However, the standard streams are character based, while arrow keys - along with shift/control/caps/Fx/etc keys - are not mapped to characters. One can't redirect an arrow-down to a file, or pipe in an F1. The low-level key-oriented input in Windows is event-based, and that's where those special keys queue each of their down/repeat/up state changes. Then, at the char-oriented high-level, the respective events are synthesized into meaningful characters as appropriate. That's how keystrokes get translated into an "a" or "A" depending on the CAPS and Shift states, or ALT+49 magically generates an "1".

I don't think that low-level keyboard input can be done all in batch. One way to cheat is powershell, and below is a hybrid batch doing just that. (Note: it requires powershell v2 to support the <# .. #> comment syntax, which comes with windows v7+ but may require a separate download http://support.microsoft.com/kb/968929 for xp.)

Code: Select all

<# :: --------------------------------------------------------------- .cmd ----
@echo off & setlocal

set "ps=" & for %%X in (powerShell.exe) do (
  set "ps=%%~$PATH:X")
if not defined ps (
  set "ps=%systemRoot%\system32\windowsPowerShell\v1.0\powerShell.exe")

echo(
echo Press ESC to exit, Ctrl-Break to abort...
echo(
:loop
for /f "tokens=1-7 delims= " %%A in (
  '^<"%~f0" "%ps%" -ExecutionPolicy bypass -NoProfile -Command -'
) do (
  echo key [%%~A %%~B]  char '%%~C'  state [%%~D %%~E]  down [%%~F %%~G]
  if '%%~A'=='27' endlocal & exit /b 0
)
goto :loop & rem ---------------------------------------------------- .ps1 --#>

# keysConverter.convertToString only used for displaying key's friendly name
Add-Type -AssemblyName System.Windows.Forms
$xvt = New-Object -TypeName System.Windows.Forms.KeysConverter

$key = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp,AllowCtrlC")

Write-Output("`"$($key.VirtualKeyCode)`"" `
+ " `"$($xvt.ConvertToString($key.VirtualKeyCode))`"" `
+ " `"$($key.Character)`"" `
+ " `"$([int]$key.ControlKeyState)`"" `
+ " `"$($key.ControlKeyState -replace `" `",`"`")`"" `
+ " `"$([int]$key.KeyDown)`"" + " `"$($key.KeyDown)`"")

exit 0
Sample output for <arrow-left><up><right><down>a<shift>a<ctrl>a<alt>49<esc>.

Code: Select all

C:\tmp>keyDown-ps.cmd

Press ESC to exit, Ctrl-Break to abort...

key [37 Left]  char ''  state [ ]  down [ ]
key [37 Left]  char ''  state [ ]  down [ ]
key [38 Up]  char ''  state [ ]  down [ ]
key [38 Up]  char ''  state [ ]  down [ ]
key [39 Right]  char ''  state [ ]  down [ ]
key [39 Right]  char ''  state [ ]  down [ ]
key [40 Down]  char ''  state [ ]  down [ ]
key [40 Down]  char ''  state [ ]  down [ ]
key [65 A]  char 'a'  state [0 0]  down [1 True]
key [65 A]  char 'a'  state [0 0]  down [0 False]
key [16 ShiftKey]  char ''  state [ ]  down [ ]
key [65 A]  char 'A'  state [16 ShiftPressed]  down [1 True]
key [65 A]  char 'A'  state [16 ShiftPressed]  down [0 False]
key [16 ShiftKey]  char ''  state [ ]  down [ ]
key [17 ControlKey]  char ''  state [ ]  down [ ]
key [65 A]  char '☺'  state [8 LeftCtrlPressed]  down [1 True]
key [65 A]  char '☺'  state [8 LeftCtrlPressed]  down [0 False]
key [17 ControlKey]  char ''  state [ ]  down [ ]
key [18 Menu]  char ''  state [ ]  down [ ]
key [37 Left]  char ''  state [ ]  down [ ]
key [37 Left]  char ''  state [ ]  down [ ]
key [33 PgUp]  char ''  state [ ]  down [ ]
key [33 PgUp]  char ''  state [ ]  down [ ]
key [18 Menu]  char '1'  state [0 0]  down [0 False]
key [27 Escape]  char '←'  state [0 0]  down [1 True]

C:\tmp>

Some notes:
- meant as a proof of concept, but quite slow for practical use;
- could be sped up by dropping the '$xvt.ConvertToString' call, and the 'add-type' along with it (the virtual key codes themselves can be looked up at http://msdn.microsoft.com/en-us/library/windows/desktop/dd375731(v=vs.85).aspx);
..even more by dropping 'IncludeKeyUp' since most times it's KeyDown that's the event of interest;
..far more by moving (some of) the loop to .ps code instead of running powershell for each individual keyboard event;
- besides the keyboard topic, the above also demonstrates a generic hybrid batch/powershell script layout;
..since powershell won't execute scripts with an extension other than .ps1, the trick in this case was to use the "-Command -" switch for using the standard input, then have the standard input redirected to read "%~f0".

EDIT: Thanks to npocmaka_ for pointing the thread at http://www.dostips.com/forum/viewtopic.php?f=3&t=5526 (sorry, not a live link since I exhausted my allowance of URLs per post already) where cmd/ps hybrids were previously discussed, including the "<# :" comment/label trick (npocmaka_), and the "powershell -c -" line to run a script from the standard input in order to work around the requirement of a .ps1 extension for actual files (siberia-man, piped-in "type"). [end edit]

Liviu

Re: Attempt to catch arrow keystrokes

Posted: 14 May 2014 03:42
by Sponge Belly
Hello All!

This post on how to mute sound volume is worth reading.

- SB