Page 1 of 1

programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 23 Aug 2014 15:22
by dbenham
I found this amazing/surprising feature at http://stackoverflow.com/q/25444765/1012053. Maybe someone can find a use for it.
Update - Scroll down to the 5th post to see how it can be used to cleanly exit from batch processing, regardless how many CALLs have been issued.

Here is a very simple TEST.BAT:

Code: Select all

@echo off
:loop
echo before
cmd /c exit -1073741510
echo after
echo(
goto loop

The Ctrl-C exit code returned from CMD /C is interpreted the same as if someone pressed the Ctrl-C key. The same effect is achieved with a return value of 3221225786.

Here is sample output with 2 iterations, first entering "n", then "y":

Code: Select all

C:\test>test
before
^CTerminate batch job (Y/N)? n
after

before
^CTerminate batch job (Y/N)? y

C:\test>


The ^C that displays can be hidden by redirecting stderr to nul. It is not the actual Ctrl-C character.

Code: Select all

@echo off
:loop
echo before
cmd /c exit -1073741510 2>nul
echo after
echo(
goto loop

sample output:

Code: Select all

C:\test>test
before
Terminate batch job (Y/N)? n
after

before
Terminate batch job (Y/N)? y

C:\test>


The ^C display is not the actual Ctrl-C character. It is actually a caret, followed by C, which is displayed on stderr when CMD.EXE detects Ctrl-C. That can be seen by redirecting stderr to stdout, and capturing the value with FOR /F.

Code: Select all

@echo off
setlocal enableDelayedExpansion
for /f %%C in ('cmd /c exit -1073741510 2^>^&1') do (
  set "str=%%C"
  echo str[0]=!str:~0,1!
  echo str[1]=!str:~1,1!
)

output:

Code: Select all

str[0]=^
str[1]=C


Dave Benham

Re: programmatically "press" Ctrl-C

Posted: 24 Aug 2014 04:28
by aGerman
Hi Dave

Thath's quite interesting even if I believe it must be some kind of a bug. I can't believe it was intention to signal that message via return value :?
I tried a few other values found at the related MSDN site yet without success ...

Regards
aGerman

Re: programmatically "press" Ctrl-C

Posted: 24 Aug 2014 06:08
by dbenham
aGerman wrote:That's quite interesting even if I believe it must be some kind of a bug. I can't believe it was intention to signal that message via return value :?
I'm not so sure... It is the error code that indicates Ctrl-C termination.

aGerman wrote:I tried a few other values found at the related MSDN site yet without success ...
I'm curious - just what kind of effect were you expecting for other return codes to indicate "success"?


Dave Benhaqm

Re: programmatically "press" Ctrl-C

Posted: 24 Aug 2014 08:18
by aGerman
dbenham wrote:
aGerman wrote:That's quite interesting even if I believe it must be some kind of a bug. I can't believe it was intention to signal that message via return value :?
I'm not so sure... It is the error code that indicates Ctrl-C termination.

I'm also not sure, but the description says "The application terminated as a result of a CTRL+C". Since you never pressed CTRL+C it's at least a bit strange from my point of view.

dbenham wrote:I'm curious - just what kind of effect were you expecting for other return codes to indicate "success"?

I actually didn't expect that anything else could affect the batch window because I assume it's just undefined behavior.
I tried with values like STATUS_FATAL_APP_EXIT and STATUS_PROCESS_IS_TERMINATING. I thought it would let the process freeze or whatever ...

Regards
aGerman

Re: programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 24 Aug 2014 09:39
by dbenham
This feature is actually quite useful :!:
:idea: It can be used to cleanly exit all batch processing, regardless how many CALLs have been issued.

In past threads, jeb has shown how a fatal syntax error can be used to exit batch processing while within a CALL (I failed to find a link). But that technique can corrupt the environment because the implicit ENDLOCAL mechanism does not work properly: See viewtopic.php?p=9199#p9199

Here is a TEST.BAT that demonstrates how the programmatic Ctrl-C "press" can be used to cleanly exit from batch processing while within a CALL.

Code: Select all

@echo off
setlocal
set test=AFTER 1st SETLOCAL
setlocal
set test=AFTER 2nd SETLOCAL
call :sub1
echo returning from main  NEVER REACHED
exit /b

:sub1
setlocal
set test=AFTER sub1 1st SETLOCAL
setlocal
set test=AFTER sub1 2nd SETLOCAL
call :sub2
echo returning from sub1 NEVER REACHED
exit /b

:sub2
setlocal
set test=AFTER sub2 1st SETLOCAL
setlocal
set test=AFTER sub2 2nd SETLOCAL
set test
call :ExitBatch
echo returning from sub2 NEVER REACHED
exit /b


:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b

sample run output:

Code: Select all

C:\test>test
test=AFTER sub2 2nd SETLOCAL

C:\test>set test
Environment variable test not defined
The entire CALL stack of SETLOCAL has been properly handled, so variable test is no longer defined upon batch termination. If a fatal syntax error were used instead of the :ExitBatch routine, then test would still be defined as AFTER sub2 2nd SETLOCAL upon batch termination.

The routine can be put into a stand-alone script named ExitBatch.bat and placed somewhere within the PATH. Then it can be conveniently used as needed by any batch script.

Code: Select all

@echo off
:ExitBatch - Cleanly exit batch processing, regardless how many CALLs
if not exist "%temp%\ExitBatchYes.txt" call :buildYes
call :CtrlC <"%temp%\ExitBatchYes.txt" 1>nul 2>&1
:CtrlC
cmd /c exit -1073741510

:buildYes - Establish a Yes file for the language used by the OS
pushd "%temp%"
set "yes="
copy nul ExitBatchYes.txt >nul
for /f "delims=(/ tokens=2" %%Y in (
  '"copy /-y nul ExitBatchYes.txt <nul"'
) do if not defined yes set "yes=%%Y"
echo %yes%>ExitBatchYes.txt
popd
exit /b


Dave Benham

Re: programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 24 Aug 2014 12:41
by einstein1969
@Dave
I don't know if the jeb method is the same for exit from a infinite for loop...

here jeb use the HALT method . Here suggest to use.

this is the method you mentioned?

Re: programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 25 Aug 2014 05:32
by jeb
@Dave

Very nice technic :D

I tried a bit to use CTRL-C for another cmd.exe thread, but currently all my attempts fail.

I tried it with pipes and also with start /b, but I haven't an idea how to transfer the CTRL-C to different cmd instance.


jeb

Re: programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 25 Aug 2014 06:10
by dbenham
jeb wrote:I tried a bit to use CTRL-C for another cmd.exe thread, but currently all my attempts fail.

I tried it with pipes and also with start /b, but I haven't an idea how to transfer the CTRL-C to different cmd instance.
Very mysterious :? :!:

I also failed with CMD /C and pipes. Of course, if one fails, than so too should the other. But I succeeded with START /B /WAIT.
Edit: Wrong! START /B /WAIT Fails as well :oops:

Here is TEST2.BAT that launches my TEST.BAT from prior post in various ways.

Code: Select all

@echo off
echo(
echo launching via CMD /C ...
cmd /c test.bat
echo Returned %errorlevel%

echo(
echo launching via pipe ...
echo OK | test.bat
echo Returned %errorlevel%

echo(
echo launching via START /B /WAIT ...
start "" /b /wait test.bat
echo Returned %errorlevel% NEVER REACHED

Sample output

Code: Select all

C:\test>test2

launching via CMD /C ...
test=AFTER sub2 2nd SETLOCAL
Returned 255

launching via pipe ...
test=AFTER sub2 2nd SETLOCAL
Returned 255

launching via START /B /WAIT ...
test=AFTER sub2 2nd SETLOCAL

C:\test>echo %errorlevel%
-1073741510

C:\test>

Why on earth do CMD /C and the pipe transform the ERRORLEVEL to 255 :?: :?
This is especially puzzling because CMD /C was used to "press" Ctrl-C in the first place :!:

START /B /WAIT appears to work, but in reality it hasn't returned to the originating CMD session yet. Issuing EXIT returns and the calling script resumes.


Dave Benham

Re: programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 25 Aug 2014 16:08
by penpen
dbenham wrote:You can see that START /B /WAIT works, although I'm a bit surprised it does not prompt to see if you want to terminate or not.

Are you sure? Try 'exit'; this happens on WinXP home:

Code: Select all

Z:\>test2.bat

launching via CMD /C ...
Returned 255

launching via pipe ...
Returned 255

launching via START /B /WAIT ...

Z:\>exit
^CBatchvorgang abbrechen (J/N)? n
Returned -1073741510 NEVER REACHED
Z:\>

The same behaviour when using 'exit /B', but it changes the errorlevel, if you specify one:

Code: Select all

(...)
launching via START /B /WAIT ...

Z:\>exit /B 2
^CBatchvorgang abbrechen (J/N)? n
Returned 2 NEVER REACHED
Z:\>

penpen

Re: programmatically "press" Ctrl-C / Exit batch inside CALL

Posted: 25 Aug 2014 22:17
by dbenham
Thanks penpen. You are absolutely correct, and jeb was correct all along (of course). I've corrected my prior post.


Dave Benham