使用 .NET 的 ANSI 着色控制台输出

ANSI-Coloring Console Output with .NET

我尝试使用 ANSI escape codes 和以下最小 C# 程序生成彩色控制台输出:

using System;

// test.cs
class foo {
    static void Main(string[] args) {
        Console.WriteLine("\x1b[36mTEST\x1b[0m");
    }
}

我是 运行 Ansicon v1.66 on Windows 7 x64 with csc.exe (Microsoft (R) Visual C# 编译器版本 4.6。 0081.0).

彩色输出在此配置下工作正常; Ansicon 本身运行完美。

为了交叉检查,我使用了一个 node.js 100% 等同于 C# 程序的单行代码:

// test.js
console.log("\x1b[36mTEST\x1b[0m");

还有更基本的手工文本文件:

两者都正确地完成了预期的事情:打印一个 teal-colored string "TEST":

只有我用 csc 构建的 test.exe 会打印其他内容。为什么?

如果您使用 AnsiCon x64 环境,您的程序需要针对 /platform:x64 进行编译;如果您使用 AnsiCon x86/32 位版本,则需要针对 /platform:x86 进行编译。确切原因成谜...

本来我以为你需要这一切:

您需要获取 StandardOutput 并让 Console.WriteLine 相信您写入文件而不是控制台并使用 ASCII 编码。

它将如何工作:

 var stdout = Console.OpenStandardOutput();
 var con = new StreamWriter(stdout, Encoding.ASCII);
 con.AutoFlush = true;
 Console.SetOut(con);

 Console.WriteLine("\x1b[36mTEST\x1b[0m");

.Net Console.WriteLine 使用内部 __ConsoleStream 检查 Console.Out 是文件句柄还是控制台句柄。默认情况下,它使用控制台句柄,因此通过调用 写入控制台。在备注中您发现:

Although an application can use WriteConsole in ANSI mode to write ANSI characters, consoles do not support ANSI escape sequences. However, some functions provide equivalent functionality. For more information, see SetCursorPos, SetConsoleTextAttribute, and GetConsoleCursorInfo.

要将字节直接写入控制台而不 WriteConsoleW 干扰一个简单的 filehandle/stream 就可以做到,这是通过调用 OpenStandardOutput 实现的。通过将该流包装在 StreamWriter 中,以便我们可以使用 Console.SetOut 再次设置它,我们就完成了。字节序列被发送到 OutputStream 并由 AnsiCon 拾取。

请注意,这仅适用于适用的终端仿真器,如 AnsiCon,如下所示:

我创建了一个 small plugin (available on NuGet),它允许您轻松地将字符串包装在 ANSI 颜色代码中。支持前景色和背景色。

它通过扩展String对象来工作,语法非常简单:

"colorize me".Pastel("#1E90FF");

之后字符串就可以打印到控制台了。

我今天遇到了这个问题,无法得到接受的答案来工作。经过我自己的一些研究后,我找到了一个可以让它工作的答案。

这很可惜,但我们需要对此进行非常低的级别并直接调用 Windows API。为此,出于方便的原因,我使用 PInvoke.Kernel32 NuGet,但如果它对你来说太 heavy-weight,你可以自己创建 P\Invoke 映射。

以下方法说明了如何激活 ANSI 代码:

bool TryEnableAnsiCodesForHandle(Kernel32.StdHandle stdHandle)
{
    var consoleHandle = Kernel32.GetStdHandle(stdHandle);
    if (Kernel32.GetConsoleMode(consoleHandle, out var consoleBufferModes) &&
        consoleBufferModes.HasFlag(Kernel32.ConsoleBufferModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING))
        return true;
    
    consoleBufferModes |= Kernel32.ConsoleBufferModes.ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    return Kernel32.SetConsoleMode(consoleHandle, consoleBufferModes);
}

要为 StdOut 启用它,您可以这样称呼它:

TryEnableAnsiCodesForHandle(Kernel32.StdHandle.STD_OUTPUT_HANDLE);

如果方法 returns 为真,则启用 ANSI 代码,否则不启用。

解决方案使用非常低的级别Windows API GetConsoleModeSetConsoleMode来检查是否设置了控制缓冲区模式ENABLE_VIRTUAL_TERMINAL_PROCESSING,如果它未设置,它尝试设置模式。