Multi-line menu with options selection via DOSKEY

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Message
Author
Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Multi-line menu with options selection via DOSKEY

#1 Post by Aacini » 31 May 2014 21:41

The creation of a multi-line menu that allows to select options via arrow keys has been an old request in Batch files. This type of selection menu may be developed with the aid of auxiliary programs, like GetKey to get the selection keys and a combination of CursorPos/ColorShow to deselect the current option and highlight the new one; however, such a program is somewhat complex.

I devised a simpler method to achieve such selection menu using DOSKEY program. This DOS command store the lines executed in the command-line (like inputs to SET /P command) and store they in a history that may be displayed in the form of a selection menu when F7 key is pressed. This way, the method consist in:

  1. Clear previous DOSKEY history.
  2. Execute several SET /P commands that read the menu options, so the DOSKEY history is filled with them.
  3. Send a F7 key to the keyboard.
  4. Execute a SET /P "OPTION=Prompt: "; the input to this command will be completed via the selection menu of DOSKEY.

Code: Select all

@if (@CodeSection == @Batch) @then


@echo off
setlocal EnableDelayedExpansion

rem Multi-line menu with options selection via DOSKEY
rem Antonio Perez Ayala

rem Define the options
set numOpts=0
for %%a in (First Second Third Fourth Fifth) do (
   set /A numOpts+=1
   set "option[!numOpts!]=%%a Option"
)
set /A numOpts+=1
set "option[!numOpts!]=exit"

rem Clear previous doskey history
doskey /REINSTALL
rem Fill doskey history with menu options
cscript //nologo /E:JScript "%~F0" EnterOpts
for /L %%i in (1,1,%numOpts%) do set /P "var="

:nextOpt
cls
echo MULTI-LINE MENU WITH OPTIONS SELECTION
echo/
rem Send a F7 key to open the selection menu
cscript //nologo /E:JScript "%~F0"
set /P "var=Select the desired option: "
echo/
if "%var%" equ "exit" goto :EOF
echo Option selected: "%var%"
pause
goto nextOpt


@end

var wshShell = WScript.CreateObject("WScript.Shell"),
    envVar = wshShell.Environment("Process"),
    numOpts = parseInt(envVar("numOpts"));

if ( WScript.Arguments.Length ) {
   // Enter menu options
   for ( var i=1; i <= numOpts; i++ ) {
      wshShell.SendKeys(envVar("option["+i+"]")+"{ENTER}");
   }
} else {
   // Enter a F7 to open the menu
   wshShell.SendKeys("{F7}");
}

Output example of previous program:

Image

There is a strange point about this program: the DOSKEY /REINSTALL command clear the history only when it is executed the first time. If previous program is executed a second time in the same cmd.exe session, the DOSKEY history is not cleared, so menu options are wrong. I read DOSKEY documentation in Microsoft and SS64 sites and didn't found any reference about this problem. I tested this program in Windows 8, so I don't know if this problem also appear in other versions...

Antonio

foxidrive
Expert
Posts: 6031
Joined: 10 Feb 2012 02:20

Re: Multi-line menu with options selection via DOSKEY

#2 Post by foxidrive » 01 Jun 2014 00:51

You're a clever fellow Antonio. :)

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: Multi-line menu with options selection via DOSKEY

#3 Post by carlos » 01 Jun 2014 02:18

Aacini: Very good idea.

Edit: for clear the buffer of command history set to 0 and then to the number of elements of the menu:

Code: Select all

rem Clear the command history
doskey /LISTSIZE=0
doskey /LISTSIZE=%numOpts%


or simple set the size to the number of elements of the menu:

Code: Select all

doskey /LISTSIZE=%numOpts%


also, i add the home key for the menu appears selected with the first option:
after F7:

Code: Select all

 wshShell.SendKeys("{HOME}");


doskey handle two buffers, the buffer of the doskey macros, and the buffer of the commands history. Apparently doskey /reinstall not works, it reset any buffer. Also /history show anything even when you see commands with F7.

Sponge Belly
Posts: 231
Joined: 01 Oct 2012 13:32
Location: Ireland
Contact:

Re: Multi-line menu with options selection via DOSKEY

#4 Post by Sponge Belly » 15 Feb 2017 16:10

Hello All! :)

Here’s an alternative approach I cobbled together:

Code: Select all

@echo off & setlocal enableextensions disabledelayedexpansion
(call;)
title Menu Demo
mode con lines=15 cols=20
(set lf=^
%= DO NOT DELETE =%
)
set ^"nl=^^^%lf%%lf%^%lf%%lf%^"
set ^"\n=^^^%lf%%lf%^%lf%%lf%^^"

cls
echo(Choose an Option:%nl%%\n%
  1. Calculator%nl%%\n%
  2. Disk Cleanup%nl%%\n%
  3. Notepad%nl%%\n%
  4. Paint%nl%%\n%
  5. WordPad%nl%%\n%
  0. Quit

:readKey
set "opt=" & for /f skip^=1^ delims^=^ eol^= %%A in ('
replace ? . /u /w
') do if not defined opt set "opt=%%A"

set opt | findstr /ix "opt=[012345]" >nul || goto readKey

if %opt% equ 0 goto end

for /f "tokens=1,2 delims=:" %%A in (
^"1:calc%nl%2:cleanmgr%nl%3:notepad%nl%4:mspaint%nl%5:wordpad^"
) do if %opt% equ %%A (
start "" "%%B.exe"
goto end
)

:end
endlocal & goto :EOF


Run the program from Windows/File Explorer. All key presses except 0-5 will be ignored.

Enjoy!

- SB

PS: Thanks to Carlos for the Read Key with REPLACE technique. 8)

guimenez
Posts: 1
Joined: 04 Feb 2016 16:36

Re: Multi-line menu with options selection via DOSKEY

#5 Post by guimenez » 16 Feb 2017 02:13

Is there anyway to disable the left and right cursor?
Because if i press does, it hide the menu and show line by line.
Thanks

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Multi-line menu with options selection via DOSKEY

#6 Post by Aacini » 18 Feb 2017 09:21

Sponge Belly wrote:Hello All! :)

Here’s an alternative approach I cobbled together:

Code: Select all

snip


Run the program from Windows/File Explorer. All key presses except 0-5 will be ignored.

Enjoy!

- SB

PS: Thanks to Carlos for the Read Key with REPLACE technique. 8)


Mmmm... As far as I know, the REPLACE technique can not read arrow keys either. Your example code could be done in a simpler way via CHOICE command...

Antonio

bakemonogatari
Posts: 21
Joined: 08 Jul 2019 05:22

Re: Multi-line menu with options selection via DOSKEY

#7 Post by bakemonogatari » 09 Jul 2019 14:30

Hello, can someone put an example of this script that would launch Explorer.exe after validating (with the Enter key) the choice "0: First option"? this would allow me to understand how to do the same for the other options. Thank you.

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Multi-line menu with options selection via DOSKEY

#8 Post by Aacini » 11 Jul 2019 22:15

I reviewed this program and fixed the problem about the reinitialization of DOSKEY history. It works correctly now.

Then, I wrote an example of a menu to start real programs. When the programs are console applications, everything is OK. However, when the programs are GUI applications, then may be a synchronization problem between the AppActivate method used to select again the cmd.exe window, and the SendKeys method used to assemble the menu. I inserted a delay between the two methods. However, there are problems now and then even if the delay is as large as 500 msec. I can't find a reliable method to achieve this goal...

Code: Select all

@if (@CodeSection == @Batch) @then


@echo off
setlocal EnableDelayedExpansion

if "%~1" equ "newCmd" goto newCmd

rem Multi-line menu with options selection via DOSKEY
rem Antonio Perez Ayala

rem Set title for AppActivate
title Select option

rem Define the options
set numOpts=0
for %%a in ("Calculator=calc" "Notepad=notepad" "Paint=mspaint" "Explorer=explorer") do (
   for /F "tokens=1,2 delims==" %%b in (%%a) do (
      set /A numOpts+=1
      set "option[!numOpts!]=%%b"
      set "app[%%b]=%%c"
   )
)
set /A numOpts+=1
set "option[!numOpts!]=Quit"


rem Options selection loop
:nextOpt
cmd /C "%~F0" newCmd
if errorlevel 1 goto nextOpt
goto :EOF


:newCmd

rem Clear previous doskey history
doskey /REINSTALL
rem Fill doskey history with menu options
cscript //nologo /E:JScript "%~F0" %numOpts%
for /L %%i in (1,1,%numOpts%) do set /P "="

cls
echo MULTI-LINE MENU WITH OPTIONS SELECTION
echo/
rem Send a F7 key to open the selection menu
cscript //nologo /E:JScript "%~F0"
rem Read the option
set /P "option=Select the desired option: "
echo/
set "continue=1"
if defined app[%option%] (
   start !app[%option%]!
) else (
   set "continue=0"
)
exit %continue%


@end

var wshShell = WScript.CreateObject("WScript.Shell"),
    envVar = wshShell.Environment("Process");

// Select the cmd.exe window
wshShell.AppActivate("Select option");
WScript.Sleep(300);

if ( WScript.Arguments.Length ) {
   // Send menu options to fill the menu
   var numOpts = parseInt(WScript.Arguments(0));
   for ( var i=1; i <= numOpts; i++ ) {
      wshShell.SendKeys(envVar("option["+i+"]")+"{ENTER}");
   }
} else {
   // Send a F7 to open the menu
   wshShell.SendKeys("{F7}");
}
Antonio


Szecska
Posts: 17
Joined: 15 Aug 2019 15:29
Location: Hungary

Re: Multi-line menu with options selection via DOSKEY

#10 Post by Szecska » 16 Aug 2019 12:22

@Sponge Belly

Code: Select all

@echo off & setlocal enableextensions disabledelayedexpansion
(call;)
title Menu Demo
mode con lines=15 cols=20
(set lf=^
%= DO NOT DELETE =%
)
set ^"nl=^^^%lf%%lf%^%lf%%lf%^"
set ^"\n=^^^%lf%%lf%^%lf%%lf%^^"

cls
echo(Choose an Option:%nl%%\n%
  1. Calculator%nl%%\n%
  2. Disk Cleanup%nl%%\n%
  3. Notepad%nl%%\n%
  4. Paint%nl%%\n%
  5. WordPad%nl%%\n%
  0. Quit

:readKey
set "opt=" & for /f skip^=1^ delims^=^ eol^= %%A in ('
replace ? . /u /w
') do if not defined opt set "opt=%%A"

set opt | findstr /ix "opt=[012345]" >nul || goto readKey

if %opt% equ 0 goto end

for /f "tokens=1,2 delims=:" %%A in (
^"1:calc%nl%2:cleanmgr%nl%3:notepad%nl%4:mspaint%nl%5:wordpad^"
) do if %opt% equ %%A (
start "" "%%B.exe"
goto end
)

:end
endlocal & goto :EOF
Your script behaves strangely if you run it from a pendrive.
It stucks at the character reading command and the same happens if you try the xcopy method on a FAT32 formatted pendrive.
I dont really know why it's happening,I guess it has to do something with the fs.

I managed to fix the issue with this:

Code: Select all

@echo off & setlocal enableextensions disabledelayedexpansion
for %%P in (%CD%) do set "OriginalDrive=%%~dP"
if not !OriginalDrive!==%SystemDrive% %SystemDrive%
REM Rest of the original code here
Szecska

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Multi-line menu with options selection via DOSKEY

#11 Post by Aacini » 18 Aug 2019 04:53

@Szecska,

As I said before, you may get the same result in a much simpler way using a choice /N /C 123450 command; I don't see any reason to use the replace.exe method here...

IMHO these replace.exe-based examples should be moved to the replace.exe explanation method thread. Those "option selection" methods that just read a key are not similar to the visual change in the selected menu option that is performed via arrow keys when doskey.exe command is used in the way described in this thread...

Antonio

carlos
Expert
Posts: 503
Joined: 20 Aug 2010 13:57
Location: Chile
Contact:

Re: Multi-line menu with options selection via DOSKEY

#12 Post by carlos » 20 Aug 2019 15:50

Szecska the replace method was a way of get an input where choice was not present in the windows. I not remeber from which version of windows it was included, maybe windows 8 ?
I'm interested anyways in the cause of fail using fat32 filesystem, maybe use:

Code: Select all

replace "%tmp%\?" "%tmp%\." /u /w
will work ?

Szecska
Posts: 17
Joined: 15 Aug 2019 15:29
Location: Hungary

Re: Multi-line menu with options selection via DOSKEY

#13 Post by Szecska » 31 Aug 2019 15:28

@Aacini
I found a strange thing lately,with this script:

Code: Select all

@echo off
:null
PowerShell Exit($host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown').VirtualKeyCode);
echo %errorlevel%
goto null
this can detect arrow keys, but only as %&'( (key 37,38,39,40)

So far this is the closest thing I could find for arrow key detection but its painfully slow.

aGerman
Expert
Posts: 4678
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Multi-line menu with options selection via DOSKEY

#14 Post by aGerman » 31 Aug 2019 16:31

Code: Select all

powershell -nop -ep Bypass -c "while($true){$ki=$host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');$vk=$ki.VirtualKeyCode;if(($ki.Character -eq 0) -and ((37..40) -contains $vk)){exit($vk-36);}}"
Maybe that's what you're looking for. Returns errorlevel 1-4.

Steffen

Aacini
Expert
Posts: 1914
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Multi-line menu with options selection via DOSKEY

#15 Post by Aacini » 01 Sep 2019 11:07

Szecska wrote:
31 Aug 2019 15:28
@Aacini
I found a strange thing lately,with this script:

Code: Select all

@echo off
:null
PowerShell Exit($host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown').VirtualKeyCode);
echo %errorlevel%
goto null
this can detect arrow keys, but only as %&'( (key 37,38,39,40)

So far this is the closest thing I could find for arrow key detection but its painfully slow.
The method to read arrow keys in an efficient way using Powershell (and also move cursor and show text in color) is described at
Read arrow keys and show color text in an efficient way thread... :roll:

PS - (All of) You should note that all this last discussion have no relation to the method to use Doskey program proposed in this thread... :?

Antonio

Post Reply