Welcome!

By registering with us, you'll be able to discuss, share and private message with other members of our community.

SignUp Now!

First start of TCMD has sometimes problems with ANSI

Jan
950
19
That's not a specific thing in v34, I had that already in v33 too but had not reported it till now.

Sometimes TCMD start has a problem with ANSI (or maybe it comes from other influence, I don't know). It seems it's after 1st start (after PC reboot) only and only with open explorer window window (if the explorer window is automatically hided it doesen't happens. Then it looks like this:

tcmd-start1-3.webp


After I pressed ENTER it's okay and stays ok:

tcmd-start2-3.webp


For further starts it looks normal:

tcmd-start3-3.webp
 
Just curious ... do you do anything in TCSTART that uses a pipe (or any transient instance)? I think TCMD just reads what's in the hidden console. TCC used to have problems where pipes could cause virtual terminal processing to be turned off briefly. I haven't seen that in quite a while. If TCSTART does use pipes, what are you piping to? Gnu apps seem notorious for messing with the console.
 
No absolutely not: this is my current start.btm:

@echo off
Code:
@echo off
rem ***** DIESE DATEI GEHÖRT INS STAMMVERZEICHNIS VON TCMD! *****
rem BESSER KEINEN PFAD MIT CDD SETZEN, DA SONST (BEI CHILD-PROZESSEN, ETC.) DIE
rem   DATEN IN EBEN DIESEM DIR UND NICHT IM CURRENT DIR ERWARTET WERDEN!
alias /r "d:\Permanent_for_PRGs_and_Batches\tc_alias.lst"

However, will make a test very soon without a start.btm ... who knows ...
 
@vefatica

Ha, maybe your hint with tcstart.btm was right - I removed it for testing - till now, no problem. How can have a loding alias list such an influence?? This list has 53 lines ... can this be the reason?

However, thank you anyway for hint.

Will investigate a bit time for this case yet (for example loading the alias list from SSD instead HDD, etc.) ...
 
Ok, seems I could fix it. At least till now it seems ok now.

What I've done:

- tcstart.btm has now just the following:
@alias /r "c:\ProgramData\JP Software\tc_alias.lst"

- Added a blank line at the end of alias list

- Moved the alias list to internal SSD drive (instead to (also internal) HDD)

Will stay tuned ...
 
I refresh this thread because it happens again ... :-(

The only workaround is to set the explorer to "automatically hide".
 
I don't know if this would help hide the issue... in my tcstart I have it do a "ver /r & cls" at the end so I get a fresh screen free of any of the output from other startup lines. That might hide the initial un-ANSI prompt.

I wonder if it has anything to do with your version of Windows as well? I haven't seen this issue on Windows 10 or Windows 11 but maybe it's language specific (you probably use the de-DE language/culture)?

I don't remember which older version it was, but when TCMD switched to using the OS provided ANSI support instead of having it built into the app, it broke my ANSI prompt (I do something similar to you, using ANSI codes in my prompt) on Windows 7 and Server 2012 R2 systems, so I started including a check in my tcstart.btm to see if I was running on one of those older OS and load a 3rd party ANSI driver. Anyway, using ANSI provided by the OS on Win10/11 systems, I just wonder if there's something language specific that makes it take a little longer to load so it's not fully initialized by the time you show your first prompt?
 
I don't know if this would help hide the issue... in my tcstart I have it do a "ver /r & cls" at the end so I get a fresh screen free of any of the output from other startup lines. That might hide the initial un-ANSI prompt.
I had tested with a "cls" already BUT your idea with additionally "ver /r" could make sense, indeed. Will test that (or other command which makes a little delay). Thanks for idea.

I wonder if it has anything to do with your version of Windows as well? I haven't seen this issue on Windows 10 or Windows 11 but maybe it's language specific (you probably use the de-DE language/culture)?

I don't remember which older version it was, but when TCMD switched to using the OS provided ANSI support instead of having it built into the app, it broke my ANSI prompt (I do something similar to you, using ANSI codes in my prompt) on Windows 7 and Server 2012 R2 systems, so I started including a check in my tcstart.btm to see if I was running on one of those older OS and load a 3rd party ANSI driver. Anyway, using ANSI provided by the OS on Win10/11 systems, I just wonder if there's something language specific that makes it take a little longer to load so it's not fully initialized by the time you show your first prompt?
Yes, that could be the case. I had different problems already because my localized Win 10 (it's a de-CH here by the way, that's even not so common than a de-DE).

However, thanks for your input. Will write the results here then ...
 
Last edited:
@MPB

I tested it with "cls" but this was then executed after calling external programs within scripts - which is no desired behaviour. So that is not practicable for me. Will make more testing with another idea of little bit delaying the starting process ... but then I will have probably the delay within scripts ... will see. Yep, that's also not practicable for me for the same reason.
 
If it's a delay with how long the native ANSI driver takes to load in a command process, maybe just adding a short "delay 5" (or however many seconds) would help, as the last line in the TCStart.BTM file. Maybe test with different delays and see what works best, starting with a higher value to see if it actually makes a difference.

In my tcstart file, I have this line at the very start so that if I'm piping output to a TCC instance, it won't load anything else:
if %_pipe EQ 1 quit

I also add this line a little further down if there are things I do want to happen when launching, like loading my custom alias and function lists, but I don't need it to set my custom prompt or other things that don't matter for a non-interactive session if I'm just calling TCC with a "tcc /c dosomething.bat":
if %_transient EQ 1 quit

Those lines make launching and running TCC so much faster in those cases, and if it turns out that a delay fixes your ANSI issues, you wouldn't want a delay for those piped or transient instances so you'd want to skip the delay in those cases. :)

EDIT: Oh, also, I meant to say that my tcstart does a "cls & ver /r" so that I clear the screen after everything in tcstart is done, and then shows the full version info at the top. It's just a little nice thing to remind me which version of TCC I have in case I'm also testing or using older versions, and I can keep track of which window is which. LOL
 
Will test that out.

Thanks very much :-)
 
@MPB

Ahhh, yep the "tricks" with "if %_pipe EQ 1 quit" and "if %_transient EQ 1 quit" fixes the undesired behaviour - I discovered the first one (at sunday or so) and I believe even the second but I didn't try it out. Very good and useful!

So, now I added the "cls & ver /r" again and ........ unfortunately I had that ANSI problem again ;-)

Will test further, no big deal because if I explorer windows is automatically hide it does always work correctly.

Will report here again, if I have news.

Thanks again for help - my start.btm is now much better anyway!
 
MAYBE I'm a step further now.

It COULD be because I changed the prompt generally - also for CMD (via user variable) - and not for TCC only. So TCMD takes this prompt from Windows user variable too (which needs certain time I think). I will test now what happens, if I EXPLICITE change/set the TCC prompt (too).
 
I don't know if this is helpful, but here's what part of my tcstart looks like where I set my prompt. I only do it if %username% is defined, but I probably don't need that as much since I added the other parts to exit if this is a piped or transient session. It includes some old code I had in there back when I might run this on a Windows 7 or Server 2012 R2 machine, and if so I'd load the separate AnsiCon driver :) I've commented it out a LONG time ago but apparently I haven't cleaned up my tcstart in several years. LOL

Besides the colorful prompt I change the window title to the computername and the current process ID so I can keep a little better track of multiple windows, and add an asterisk if this is an elevated session so I know to be more careful in there. :)

Code:
iff defined USERNAME then
  rem *** Windows versions less than 10 (or Server 2016) don't have built-in ANSI support, and TCC doesn't use its own routine anymore
  rem *** Add in AnsiCon if needed on older OS to support ANSI routines in a command prompt
  rem *** Commenting out since Win7 / Server 2012 are no longer supported anyway
  rem if %_winver LT 10 %~dp0AnsiCon\AnsiCon.exe -p
  prompt $e[33;40;1m[$e[36;40;1m$P$e[33;40;1m]$e[37;40;1m
endiff
rem *** make sure the CLI text is white on black
color bri white on black
rem *** Give the window title something unique
set titleprompt=%computername - %_pid
if %_elevated EQ 1 set titleprompt=*%titleprompt
 
Ok, I set now the prompt explicite for TCC within start.btm - same problem.

Will try now with an "delay /M 200" (200 millisec) at the end of start.btm ...
 
I have had this problem also. I have long thought that the problem comes from VT processing being momentarily disabled (I think it should always be enabled). As time goes by I have the problem less; I haven't seen it in months. That might be because I enable VT processing in plugins that I use.

So I wrote a new tiny, single-purpose plugin called VTFIX. It works like this.

When it loads it enables VT processing. When it unloads it enables VT processing. If the loading is automatic (plugin DLL in <TCCHome>\Plugins\) this will happen with every instance of TCC.

It also exports one command, ENABLEVT (no input, no output, no help) which enables VT processing on demand. In addition to loading it automatically, I have IF ISPLUGIN ENABLEVT (ENABLEVT) at the beginning of my TCSTART.BTM.

If you want to try it, get it here:

Code:
copy ftp://vefatica.net/4plugins/x64/vtfix.zip

I'd be interested in knowing if it helps.
 
@vefatica

Thank you very much too for your help!

I will try now first further with my following current start.btm ...

Code:
@echo off
rem Next command ensures that following things are not executed again if pipe is in use
if %_pipe EQ 1 quit
rem Next command ensures that following things are not executed again if it's a transient instance (external commands (for ex.))
if %_transient EQ 1 quit
alias /r "d:\Permanent_for_PRGs_and_Batches\tc_alias.lst"
if not %_elevated EQ 1 prompt $e[1;34m%USERNAME%$e[1;37m@$e[1;34m%USERDOMAIN%$s$e[1;33m$g$e[1;36m[$e[1;33m"$e[1;37m$p$e[1;33m"$e[1;36m]$_$e[1;37mADM:$s$e[0;37mN$s$e[1;36m$b$s$e[1;37mZeit:$s$e[0;37m$t$h$h$h$s$e[1;36m$b$s$e[0;37m$e[1;35m$$$s$e[0;37m
if %_elevated EQ 1 prompt $e[1;34m%USERNAME%$e[1;37m@$e[1;34m%USERDOMAIN%$s$e[1;33m$g$e[1;36m[$e[1;33m"$e[1;37m$p$e[1;33m"$e[1;36m]$_$e[1;37mADM:$s$e[0;37mJ$s$e[1;36m$b$s$e[1;37mZeit:$s$e[0;37m$t$h$h$h$s$e[1;36m$b$s$e[0;37m$e[1;35m#$s$e[0;37m
rem @cls & ver
rem @echo ``
delay /M 200
rem echo %_tctabs
if %_tctabs GE 1 CDD /T %USERPROFILE%\Downloads
if %_tctabs LT 1 CDD %USERPROFILE%\Downloads
delay /M 200

... which seems to run really good till now (the delay of 200 ms is almost not noticable).

If that fails too, I will immediately test further with your file (downloaded already!

EDIT: I added a second "delay /M 200" because it was needed for correct working with internal variables like "_tctabs" within "tcstart.btm* - else it was too fast and "_tctabs" value was sometimes 1, sometimes 0 or even -1 after I started TCMD. With that delay it works here perfectly it seems and "_tctabs" is is always = 1.

EDIT2: It seems definitive better at least now (with the added delays (see code above)) - made different PC reboots and started TCMD maybe 20 times (probably more), it never happened again TILL NOW ... JFI ... I'm not absolutely sure about it ;-)

EDIT3: Seems really solved now - had so many times started TCMD for different other things too ... it NEVER happened again.
 
Last edited:
JFI:

I try now with one and shorter delay only ... note that the "echo %_tctab" (if it's active) could then give a false value, but it seems that the following "if's" still working right.
The shorter delay makes a smooter opening of tabs (here) ...

Code:
@echo off
alias /r "d:\Permanent_for_PRGs_and_Batches\tc_alias.lst"
rem Next command ensures that following things are not executed again if pipe is in use
if %_pipe EQ 1 quit
rem Next command ensures that following things are not executed again if it's a transient instance (external commands (for ex.))
if %_transient EQ 1 quit
if not %_elevated EQ 1 prompt $e[1;34m%USERNAME%$e[1;37m@$e[1;34m%USERDOMAIN%$s$e[1;33m$g$e[1;36m[$e[1;33m"$e[1;37m$p$e[1;33m"$e[1;36m]$_$e[1;37mADM:$s$e[0;37mN$s$e[1;36m$b$s$e[1;37mZeit:$s$e[0;37m$t$h$h$h$s$e[1;36m$b$s$e[0;37m$e[1;35m$$$s$e[0;37m
if %_elevated EQ 1 prompt $e[1;34m%USERNAME%$e[1;37m@$e[1;34m%USERDOMAIN%$s$e[1;33m$g$e[1;36m[$e[1;33m"$e[1;37m$p$e[1;33m"$e[1;36m]$_$e[1;37mADM:$s$e[0;37mJ$s$e[1;36m$b$s$e[1;37mZeit:$s$e[0;37m$t$h$h$h$s$e[1;36m$b$s$e[0;37m$e[1;35m#$s$e[0;37m
rem @cls & ver
rem @echo ``
delay /M 50
rem echo %_tctabs
if %_tctabs GE 1 CDD /T %USERPROFILE%\Downloads
if %_tctabs LT 1 CDD %USERPROFILE%\Downloads
rem delay /M 50

EDIT: alias loading is now earlier: before "if pipe" & "if transient" to ensure that it's always loaded (had problems with certain script else) ...
 
Last edited:
Code:
copy ftp://vefatica.net/4plugins/x64/vtfix.zip

I'd be interested in knowing if it helps.

So now it's my turn to ask you: How the hell did you get it so tiny? Did you write this one in hand-optimized assembler, or what?

I wrote an even more minimal version (no internal command, just sets the console mode and exits). Even after UPXing, mine weighs in at just under 20K. Yeah, I have a VERSIONINFO resource, but that only adds about 1K. So why is mine so obese compared to yours?
 
Most of the difference is a custom entry point, which is OK as long as you're not using any C library stuff. The same caveats as DllMain apply; only use stuff in DLLs you know are loaded (notably, kernel32). Apparently, a custom entry point gets the same args as DllMain. This works.

Code:
BOOL WINAPI MyEntry (HINSTANCE hInstance, DWORD dwReason, PVOID pvReserved)
{
    if ( dwReason == DLL_PROCESS_ATTACH )
    {
        pluginfo.hModule = (HMODULE) hInstance;
    }
    return TRUE;
}

Also, No debug info, no manifest, merge .pdata=.data, and the linker option /NOCOFFGRPINFO. I think that's it. I set them all in the properties dialog. There are probably pragmas to do them.
 
Thank you. I suspect I could learn a lot from you....
I have a modest collection of tidbits that I've collected over ~30 years. I am not formally trained in programming. I only got a few chapters into Kernighan & Ritchie (early 1990s) before I could do the things I wanted to do. Then I discovered the Win32 API which let me do a lot of fun things. Long before that, I spent a few years with The National Cash Register Company (later NCR Corp.) writing custom accounting apps for smallish (by old standards) computers and smallish businesses. I took pretty little from that experience, except for "debits by the window credits by the door" (or was that the other way around?). In grad school, I learned enough Algol and True Basic to do some graph theoretical stuff.
 
Most of the difference is a custom entry point, which is OK as long as you're not using any C library stuff. The same caveats as DllMain apply; only use stuff in DLLs you know are loaded (notably, kernel32). Apparently, a custom entry point gets the same args as DllMain. This works.

My word. That does shrink it down dramatically.
 
My word. That does shrink it down dramatically.
Yup. DllMain is a C thingy. It does all sorts of initialization of C stuff. The details are unkonwn to me. But without it you can't use C library stuff. FWIW, these (below) work. I couldn't find a way to get the other things I mentioned into pragmas or defines.

Code:
#pragma comment(linker, "/ENTRY:MyEntry")
#pragma comment(linker, "/MERGE:.pdata=.data")

Depending on the sizes of the sections, the merge may not save the 512 bytes that a section takes. You can merge other sections but some merges are disallowed, some cause the introduction of new sections (.idata or .xdata), and some cause loading the DLL to fail.

With the same warnings you can also use a custom entry point in an EXE. I don't know if argc and argv will be set but you can get the wide counterparts with CommandLineToArgvW(GetCommandLine(), ...). I often use that to parse (argv-style) the LPWSTR argument to plugin functions.
 
Thanks. I am not obsessed with file sizes, but seeing my "minimal" plugin at 5½ times the size of yours was just embarrassing. Looks like this now:

Code:
C:\Bin\JPSDK\SetVT>dir /a /s /m *.dll

 Volume in drive C is Windows      Serial number is 966f:a693

 Directory of  C:\Bin\JPSDK\SetVT\Release\*.dll

 2/27/2025   9:04           4,096  !SetVT-x86.dll

 Directory of  C:\Bin\JPSDK\SetVT\x64\Release\*.dll

 2/27/2025   9:04           6,144  !SetVT.dll

C:\Bin\JPSDK\SetVT>

That's with a version info resource and no compression, so not so shameful.
 
That's good. But I wonder why the x86/x64 difference is as large as it is. Is there a lot of text in it? Did you omit the manifest and debug info in both, and use the /NOCOFFGRPINFO linker option in both?
 
That's good. But I wonder why the x86/x64 difference is as large as it is. Is there a lot of text in it? Did you omit the manifest and debug info in both, and use the /NOCOFFGRPINFO linker option in both?

I don't really know where the difference in code size comes from; there's not a lot of code in it. Yes, I did disable both the manifest and the debug info. /NOCOFFGRPINFO made no apparent difference, so I took it back out again.

There is almost no text in the thing: just the filename "conout$", the plugin name "SetVT", and all the version info stuff. The PLUGININFO block is downright laconic:

Code:
DLLExports LPPLUGININFO WINAPI GetPluginInfo( void )
{
    /*
            No point in providing all this info, as this plugin does not remain resident
            in memory!  Anyone who wants version info, etc. will just have to read it
            from the VERSIONINFO resource.
    */

    static PLUGININFO piInfo;

    piInfo.pszDll            = PLUGIN_NAME;
    piInfo.pszAuthor         = L"";            //    L"Charles Dye";
    piInfo.pszEmail          = L"";            //    L"[email protected]";
    piInfo.pszWWW            = L"";            //    L"https://charlesdye.net/plugins/setvt.html";
    piInfo.pszDescription    = L"";            //    L"Enable console Virtual Terminal processing";
    piInfo.pszFunctions      = L"";
    piInfo.nMajor            = VER_MAJOR;
    piInfo.nMinor            = VER_MINOR;
    piInfo.nBuild            = VER_BUILD;

    return &piInfo;
}
 
Back
Top