custom console window banner

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
lazna
Posts: 62
Joined: 27 Dec 2012 10:54

custom console window banner

#1 Post by lazna » 23 Dec 2023 19:19

If autorun script installed in 'HKLM\SOFTWARE\Microsoft\Command Processor\AutoRun', want to announce it by custom console window simple textual banner. It should appear in the widows header once, just after window is started.

As far as text printed by 'echo' command could not be used (because it breaking for loop), experimenting with 'prompt'.

Finaly create two scripts and install into:

Code: Select all

HKLM\SOFTWARE\Microsoft\Command Processor\Autorun
and

Code: Select all

HKCU\SOFTWARE\Microsoft\Command Processor\Autorun
the one in HKLM contain

Code: Select all

@echo off
cls
prompt $s$s$s$s$sEnhanced console activated$_$s$s$s$s$sFor help type 'help ec'$_$_$p$e[1;31;42mEC$e[0m$g
and the one in HKCU contain

Code: Select all

prompt $p$g
Banner printing in first script work fine, prompt change (to avoid printing banner again) from second file also work fine, but command itself is printed too.

My results:

Code: Select all

     Enhanced console activated
     For help type 'help ec'

C:\Users>prompt $p$e[1;31;42mEC$e[0m$g

C:\Users>

Prepending line '@echo off' or just '@' before prompt command broke whole concept?

Could someone clarify how both autorun scripts are processed? On what oder? Or are they combined before run? Or anyone have completelly different idea?
Last edited by lazna on 24 Dec 2023 10:10, edited 1 time in total.

lazna
Posts: 62
Joined: 27 Dec 2012 10:54

Re: custom console windows banner

#2 Post by lazna » 24 Dec 2023 05:33

UPDATE:

Seems even multiline prompt break scripts too :-/ So it looks this concept is wrong from scratch.

Any other idea?

jeb
Expert
Posts: 1055
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: custom console window banner

#3 Post by jeb » 25 Dec 2023 10:14

Hi lazna.
lazna wrote:
24 Dec 2023 05:33
Seems even multiline prompt break scripts too :-/ So it looks this concept is wrong from scratch.
The obvious way: Don't break scripts by not outputting anything. Output your banner only when you are not in a script.
Sounds simple, but then you have to solve the problem of the decision who called the autorun script.

But this can be solved (partly) by check the cmdcmdline variable.
The opening lines show how this can be coded, the rest of the code solves the drag&drop problems of windows and implements an argument parser.

Code: Select all

@echo off
REM *** To enable this script, call it by <scriptName> --install

REM *** To see the current values
REM *** reg query "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun
REM *** reg query "HKEY_LOCAL_MACHINE\\Software\Microsoft\Command Processor" /v AutoRun

REM *** To delete the keys
REM *** reg DELETE "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun /f
REM *** reg DELETE "HKEY_LOCAL_MACHINE\\Software\Microsoft\Command Processor" /v AutoRun /f

setlocal EnableDelayedExpansion
REM *** ALWAYS make a copy of the complete CMDCMDLINE, else you destroy the originial!!!
set "_ccl_=!cmdcmdline!"
echo(>CON

REM *** %1 contains only data, when the script itself was called from the command line
if "%~1" NEQ "" (
	goto :direct_call
)
REM echo #0 '!_ccl_!' > CON
REM *** The check is necessary to distinguish between a new cmd.exe instance for a user or for a "FOR /F" sub-command
if "!_ccl_:~1,-2!" == "!comspec!" (
	REM ***** INTERACTIVE ****
	
	echo %DATE% %TIME% comspec >> "%~dp0\started.log"
	FOR %%M in ("%~dp0\cmdMacros.mac") DO (
	  	endlocal	
		echo ********************************************************************
		echo * AutoRun executed from "%~f0"
		if exist "%%~M" (
			doskey /macrofile="%%~M"
			echo * Macros loaded from "%%~M"
		) ELSE (
			echo * Macrofile missing at "%%~M"
		)
		echo ********************************************************************
	)
	REM set "path=%path%;%~dp0"
	
	SET "BATLIB=C:\Project\BatchLibrary"

	call cd /D "%%BATLIB%%"
) ELSE (
	REM *** This is a FOR command, a call by an explorer click or a drag & drop operation

	REM *** Try to detect a PROBLEMATIC Drag&Drop operation, if it has the format `%comspec% /c ""`
	REM *** It's only problematic when there is one & in the data
	REM *** But it's not bullet proof, FOR /F in ('""C:\temp\myfile.bat" C:\temp\file&second.bat"') can't be distinguished

	REM *** First check, that the cmdline starts with `%comspec% /c ""....`
	if "!_ccl_!" NEQ "!_ccl_:*/c ""=#!" if "!comspec! /c ""!_ccl_:*/c ""=!" == "!_ccl_!" (
		REM *** Only handle it, if there is at least one & and no pipe |
		if "!_ccl_!" NEQ "!_ccl_:&=!" if "!_ccl_!" EQU "!_ccl_:|=!" (
			call :DRAG_and_drop
			%DEBUG_DRAG% echo # BACK from DRAG_and_drop
		)
	)
	REM *** Leaving now
	REM *** This is a "FOR /F" sub-command 
	REM *** or a "harmless" drag&drop 
	REM *** or the second run of a problematic drag&drop
	REM echo %DATE% %TIME% internal "!_ccl_!" >> "%~dp0\started.log"
	endlocal
)

exit /b

:DRAG_and_drop
REM *** This results into recursive execution of this script
REM *** Be sure, that it's not an endless recursion

REM *** Remove the front <%comspec% /c ">
REM set "_ccl_=!_ccl_:&=+!"
set "_ccl_=!_ccl_:~0,-1!"
set "_ccl_=!_ccl_:*/c "=!"
set "DEBUG_DRAG=REM " 

%DEBUG_DRAG% echo #A '!_ccl_!' > CON

call :arg_parser argv _ccl_ 
 
REM *** Check if there is at least one unquoted, problematic &
REM *** This arg must not have spaces
set "isDragAndDrop="

if defined argv.onlySingleSpace (
	set "isDragAndDrop=1"
	set "problematic="
	set "args="
	FOR /L %%# in (0 1 !argv#-1!) DO (
		set "arg=!argv[%%#]!"
		REM *** Check if it's an absolute path
		FOR /F "tokens=1,*" %%1 in (": !arg!") DO (
			if "%%~f2" NEQ "%%~2" (
				set "isDragAndDrop="
				%DEBUG_DRAG% echo *** 'NO PATH' !arg! NEQ %%~f2 > CON
				exit /b
			)
		)

		if defined argv[%%#].quoted (
			set "arg="!arg!""
		) ELSE (
			REM *** If not quoted but contains a &
			
			if "!arg!" NEQ "!arg:&=!" (
				if "!arg:~1,2!" EQU ":\" (
					set "arg="!arg!""
					set "problematic=1"				
				) ELSE (
					REM *** This can't be drap&drop, because arg doesn't begin with "<drive>:\"
					set "isDragAndDrop="
					%DEBUG_DRAG% echo *** NO DRIVE '!arg!' > CON
					exit /b
				)
			)
		)
		set "args=!args!!arg! "
	)
	set "args=!args:~,-1!"
)

if not defined problematic (
	set "isDragAndDrop="
	REM echo *** 'problematic = 0' > CON
)

if defined isDragAndDrop (
REM ECHO *** HANDLE isDragAndDrop *** > CON
REM echo *** '!args!' > CON
	REM *** The TWO SPACES between comspec and /c are essential!
	REM *** The AutoRun script detects the recursion call by this difference
	(
		endlocal
		%comspec%  /c ^"%args%"
	)

	REM *** Back from the dragDrop script
	REM *** EXIT to avoid a second call
	exit
)

REM *** This is the path for unproblematic content
REM *** Or it was detected, that this isn't a drap&drop operation
exit /b

:direct_call
if "%~1" == "--install" (
	reg add "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v "AutoRun" /t REG_SZ /d "%~f0"
	exit /b
) 

if "%~1" == "--show" (	
	reg query "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun
	exit /b
)

if "%~1" == "--remove" (
	reg DELETE "HKEY_CURRENT_USER\Software\Microsoft\Command Processor" /v AutoRun /f
)
exit /b

:arg_parser <return_array> arg_line
REM *** @TODO: ENDLOCAL all arg-... variables
::: Output arg0, ..., arg<n> and arg#
::: arg0 ... arg<n> contains unquoted arguments
::: an argument that contains only one quote (last character in line) is treated as quoted  arg=" -> arg=<empty>, arg.quoted=1
::: arg0.quoted contains 1 if the argument was quoted
::: \ escapes the next character (also inside quotes), can escape delimiters outside quotes, can escape quotes
::: Only \" is replaced to a single quote ", all other \<char> are unchanged

set "var=%1"
(set^ arg_line=!%2!)
call :strlen arg_len arg_line
set /a %var%_len-=1

set "quoted="
rem set "escapeChar="   --- "Say: \"Hello\" to frank\n"
set /a %var%#=0
set "%var%[0]="
set "%var%.onlySingleSpace=1"
for /L %%I in (0 1 !arg_len!) DO (
	for %%# in (!%var%#!) DO  (
		set "char=!arg_line:~%%I,1!"
		set "isDelim="

		if "!escapeChar!!char!" == ^""" set "quoted=!quoted:~,0!"

		REM echo %%I: "!char!" quote: !quote!
		%= only outside quotes =%
		%= check if char is a delim =%
		if not defined quoted (
			FOR /F "tokens=1,2 delims= " %%D in ("isDelim!char!NO") DO (
				if "%%D" == "isDelim" (
					set isDelim=1
					if "!arg_line:~%%I,2!" == "  " (
						%DEBUG_DRAG% echo ##### NOT onlySingleSpace > CON
						set "%var%.onlySingleSpace="
					)
				)
			)
		)

		REM *** Split arguments by not escaped delimiter
		if "!isDelim!,!quoted!,!escapeChar!" == "1,," (
			REM *** Finalize arg, test if arg is quoted
			if defined %var%[%%#] (
				set %var%[%%#].quoted=
				REM echo    #in %%# "!%var%[%%#]:~,1!!%var%[%%#]:~-1!"
				if "!%var%[%%#]:~,1!!%var%[%%#]:~-1!" == """" (
					if "!%var%[%%#]:~-2,-1!" NEQ "\" (
						set %var%[%%#].quoted=1
						set "%var%[%%#]=!%var%[%%#]:~1,-1!"
					)
				)
				REM * NOT USED HERE * if defined %var%[%%#] (
				REM * NOT USED HERE * 	set ^"%var%[%%#]=!%var%[%%#]:"\"="!"
				REM * NOT USED HERE * )
				set /a %var%#+=1
			)
		)

		REM * NOT USED HERE * if defined escapeChar (
		REM * NOT USED HERE * 	set "escapeChar="
		REM * NOT USED HERE * 	if "!char!" EQU ^""" (
		REM * NOT USED HERE * 		REM *** Replace escaped quotes with "\" that can be safely detected and replaced later
		REM * NOT USED HERE * 		set "char="\""
		REM * NOT USED HERE * 	) ELSE IF defined isDelim (
		REM * NOT USED HERE * 		set "isDelim="
		REM * NOT USED HERE * 	) ELSE (
		REM * NOT USED HERE * 		set "char=\!char!"
		REM * NOT USED HERE * 	)
		REM * NOT USED HERE * ) ELSE if "!char!" == "\" set "escapeChar=\"

		if defined quoted set "isDelim="

		REM *** Append char to arg
		if "!isDelim!!escapeChar!" == "" (
			set "%var%[%%#]=!%var%[%%#]!!char!"
		)
	)
)

for %%# in (!%var%#!) DO (
	REM *** Last character is \
	if defined escapeChar (
		set "%var%[%%#]=!%var%[%%#]!\"
	)

	REM *** Duplicated code
	REM *** Finalize arg, test if arg is quoted
	if defined %var%[%%#] (
		set %var%[%%#].quoted=
		if "!%var%[%%#]:~,1!!%var%[%%#]:~-1!" == """" (
			if "!%var%[%%#]:~-2,-1!" NEQ "\" (
				set %var%[%%#].quoted=1
				set "%var%[%%#]=!%var%[%%#]:~1,-1!"
			)
		)
		REM * NOT USED HERE * if defined %var%[%%#] (
		REM * NOT USED HERE * 	set ^"%var%[%%#]=!%var%[%%#]:"\"="!"
		REM * NOT USED HERE * )
		set /a %var%#+=1
	)
)
REM *** Create a helper variable with name "arg#-1"
set /a tmp=!%var%#!-1
set "%var%#-1=!tmp!"

exit /b
::: detector line %~* *** FATAL ERROR: missing parenthesis or exit /b
:strlen <resultVar> <stringVar>
(   
    setlocal EnableDelayedExpansion
    (set^ tmp=!%~2!)
    if defined tmp (
        set "len=1"
        for %%P in (4096 2048 1024 512 256 128 64 32 16 8 4 2 1) do (
            if "!tmp:~%%P,1!" NEQ "" ( 
                set /a "len+=%%P"
                set "tmp=!tmp:~%%P!"
            )
        )
    ) ELSE (
        set len=0
    )
)
( 
    endlocal
    set "%~1=%len%"
    exit /b
)

jfl
Posts: 226
Joined: 26 Oct 2012 06:40
Location: Saint Hilaire du Touvet, France
Contact:

Re: custom console window banner

#4 Post by jfl » 17 Jan 2024 08:10

lazna wrote:
23 Dec 2023 19:19
Banner printing in first script work fine, prompt change (to avoid printing banner again) from second file also work fine, but command itself is printed too.
If the goal is to print a banner once, why not simply echo that banner once in a single autorun script, instead of changing the prompt twice in two distinct autorun scripts?

Note that defining your own autorun scripts is risky, because someone else might have had the same bad idea, and overwrite your autorun registry entry when you install their application.
Unix distributions resolve this same issue by providing standardized autorun scripts, which execute all scripts found in a specific subdirectory.
This allows adding your own scripts in that subdirectory with little risk of colliding with someone else's.
I wrote an AutoRun.cmd script for Windows which does the same, looking for scripts in "%ALLUSERSPROFILE%\AutoRun.cmd.d\" and "%USERPROFILE%\AutoRun.cmd.d\" directories.
Using it, I now have several autorun scripts that all run when a cmd.exe starts.
The problem is that all this depends on the above AutoRun.cmd script... which never got standardized by Windows, and probably never will.
Back to square one.

lazna
Posts: 62
Joined: 27 Dec 2012 10:54

Re: custom console window banner

#5 Post by lazna » 21 Jan 2024 11:29

thank you both, will check your solutions!

lazna
Posts: 62
Joined: 27 Dec 2012 10:54

Re: custom console window banner

#6 Post by lazna » 25 May 2024 06:44

just tried jebs solution. It printing banner fine, but it prepend %a token with strange character

this is an autorun script

Code: Select all

@echo off
cls
set "_ccl_=%cmdcmdline%"
if "%_ccl_:~1,-2%" == "%comspec%" (
			echo  This is the banner first line
			echo  the second line...
			) 
when start a new console window, it print banner just one correctly. But in this loop

Code: Select all

for /f %a in ('time /t') do echo "%a"
it print

Code: Select all

"♀14:33"
when disable autorun in registry, start new cmd window, than run autorun script manually there and than run loop again, no strange character present. CHCP 852.

any ideas?

Post Reply