Home > other >  Recolour entire terminal in C#
Recolour entire terminal in C#

Time:09-29

In a program I am writing, I want the entire console background to turn blue when there is an error. In Python or C this is easily achieved with os.system("color 17"). How can I do this in C#?

The code I have been trying seems to execute the command in a different window and has no effect on the main one.

public static int system(String command)
        {
            Process cmd = new Process();
            cmd.StartInfo.FileName = "cmd.exe";
            cmd.StartInfo.RedirectStandardInput = true;
            cmd.StartInfo.RedirectStandardOutput = true;
            cmd.StartInfo.CreateNoWindow = false;
            cmd.StartInfo.UseShellExecute = false;
            cmd.Start();

            cmd.StandardInput.WriteLine(command);
            cmd.StandardInput.Flush();
            cmd.StandardInput.Close();
            cmd.WaitForExit();
            Console.WriteLine(cmd.StandardOutput.ReadToEnd());
            return cmd.ExitCode;
        }

and then calling this with system("color 17")

I have tried flipping some of the cmd.StartInfo arguments but that just seems to crash the program.

I am using Microsoft .NET 6.0 on Windows 11 using Visual Studio 2022 if that helps at all

CodePudding user response:

Don't use external applications, this isn't the 80s. Especially for something as trivial as console buffer manipulation.

.Net actually comes with Console.BackgroundColor and Console.ForegroundColor built-in to set the character attributes for new characters, so all that's missing is changing existing characters in the buffer to the required attributes (which is what color does). The function for that is FillConsoleOutputAttribute.

So putting it together, the code to achieve what you wish is simply:

public static void Main()
{
    var stdout = GetStdHandle(-11);
    FillConsoleOutputAttribute(stdout,
        CharAttributes.BACKGROUND_BLUE | CharAttributes.FOREGROUND_BLUE | CharAttributes.FOREGROUND_RED | CharAttributes.FOREGROUND_GREEN,
        (uint)(Console.BufferWidth * Console.BufferHeight), new(), out var _);

    Console.BackgroundColor = ConsoleColor.DarkBlue;
    Console.ForegroundColor = ConsoleColor.Gray;
}

With a couple of P/Invoke definitions:

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

enum CharAttributes : ushort
{
    None = 0x0000,
    FOREGROUND_BLUE = 0x0001,
    FOREGROUND_GREEN = 0x0002,
    FOREGROUND_RED = 0x0004,
    FOREGROUND_INTENSITY = 0x0008,
    BACKGROUND_BLUE = 0x0010,
    BACKGROUND_GREEN = 0x0020,
    BACKGROUND_RED = 0x0040,
    BACKGROUND_INTENSITY = 0x0080,

    COMMON_LVB_LEADING_BYTE = 0x0100,
    COMMON_LVB_TRAILING_BYTE = 0x0200,
    COMMON_LVB_GRID_HORIZONTAL = 0x0400,
    COMMON_LVB_GRID_LVERTICAL = 0x0800,
    COMMON_LVB_GRID_RVERTICAL = 0x1000,
    COMMON_LVB_REVERSE_VIDEO = 0x4000,
    COMMON_LVB_UNDERSCORE = 0x8000,
}

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool FillConsoleOutputAttribute(IntPtr hConsoleOutput, CharAttributes wAttribute, uint nLength, COORD dwWriteCoord, out uint lpNumberOfAttrsWritten);

[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
  • Related