現在のWindows 10/11の標準コンソール(conhost.exe)やWindows Terminal、その他の端末エミュレーターソフトやコンソールアプリケーションの大半がANSIエスケープシーケンスにより画面制御が行える。
ANSIエスケープシーケンスは、Escコードを先頭にASCII文字からなる簡単なものなので、これを文字列に含ませることで、カーソル制御や表示属性の変更が可能だ。
WSLのLinux(bash)やPowerShell(Windows PowerShellもPowerShell Coreのどちらも)も文字列にEscコードを埋め込むことができる。しかし、残念なことにcmd.exeの内部コマンド(cmd.exeが処理するコマンド)のechoやWindows付属のコマンドには、エスケープシーケンスを表示できるものがない。
【追記】ダウンロードリンクを追加(2022年1月28日)
まずは、Windows Terminalなど、標準でANSIエスケープシーケンスが有効な場合向けのC#プログラムだ(ダウンロードリンクはページの最後)。
using System; using System.Text.RegularExpressions; namespace zomelak { class Program { static void Main(string[] args) { Console.WriteLine(Regex.Unescape(args[0]+@"\e[0m")); } } }プログラムとしては単純で、1つめの引数(C#だとArgs[0])をRegexのUnescapeメソッドでエンコードする。C#では、“\e”などのエスケープ文字を文字列リテラルに記述できるが、ソースコードでは指定できても実際の値に変換するのはコンパイルのときなので、実行中にエスケープ文字を解釈することはない。このため、実行中に外部からもらう文字列は、エスケープ処理などが行われない。これを行うのが.NETの正規表現オブジェクト(Regex)のUnescapeメソッドだ。
Regex.Unescape(String) メソッド (System.Text.RegularExpressions)
Windows標準コンソールで表示
次は、標準コンソール用だが、基本的な考えは同じ。ただし、標準コンソールはデフォルトでは、ANSIエスケープシーケンスを解釈しないモードになっている。これを有効にするには、Win32APIのSetConsoleMode関数を使って、“ENABLE_VIRTUAL_TERMINAL_PROCESSING”(値としては0x0004)フラグを立ててやる必要がある。しかも、他のフラグが立ってるかもしれないので、現在のモードをGetConsoleModeで取得する必要がある。また、どちらの関数もコンソールウィンドウの標準出力ハンドルが必要である。というわけで3つのWin32APIを定義している(リストの8~10行目)。
using System; using System.Text.RegularExpressions; using System.Runtime.InteropServices; namespace azarak { class Program { [DllImport("kernel32.dll", SetLastError = true)] static extern IntPtr GetStdHandle(int nStdHandle); [DllImport("kernel32.dll", SetLastError = true)] static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); [DllImport("kernel32.dll", SetLastError = true)] static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode); private const int STD_OUTPUT_HANDLE = -11; private const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004; static void Main(string[] args) { IntPtr handle=GetStdHandle(STD_OUTPUT_HANDLE); uint dmode; GetConsoleMode(handle,out dmode); dmode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; SetConsoleMode(handle,dmode); Console.WriteLine(Regex.Unescape(args[0]+@"\e[0m")); } } }
それぞれの関数は、以下に解説がある。
これらの関数は、エラーが起きる可能性があり、エラー値を戻す。手抜きだが、エラー処理は無視。どうせエラーが起きたら何もできないからどうしようもない。
ダウンロード
以下にそれぞれのzip圧縮ファイルのリンクを示す
0 件のコメント:
コメントを投稿