Custom text color in C# console application?
Asked Answered
P

7

39

I just finished my C# console application code for a project and would like to add some color to my font. I would love to be able to use a custom color - orange. Is there any way to do this?

This is the code I have been using to change colors in the past, but it doesn't offer orange:

Console.ForegroundColor = ConsoleColor.Magenta(and so on);

Is there a way to maybe insert a hex value for the color or something similar?

Pardo answered 29/10, 2011 at 5:53 Comment(2)
edited my answer below to include this link. support.microsoft.com/kb/319883 shows blue and red making purple.. just use red and yellow to make orange.Trenton
my previous answer was deficient as it didn't allow you to overwrite the 16 predefined colors, I found the true answer over at the pinvoke.net and updated my answer below with the wall of code. pinvoke.net/default.aspx/kernel32.SetConsoleScreenBufferInfoExTrenton
T
33

The list found at http://msdn.microsoft.com/en-us/library/system.console.backgroundcolor.aspx

I believe are the only supported colors in console. No hex allowed.

Black
DarkBlue
DarkGreen
DarkCyan
DarkRed
DarkMagenta
DarkYellow
Gray
DarkGray
Blue
Green
Cyan
Red
Magenta
Yellow
White

EDIT

Get the working project files off my public Repo

https://bitbucket.org/benskolnick/color-console/

But on further investigation you can do a lot of work to combine red and yellow to get orange. Follow the example here. Not going to re-post wall of code. http://support.microsoft.com/kb/319883 That Doesn't give you access to more colors but does lead in the correct direction. You will need to do some PINVOKE work but I was easily able to get orange, or any other RGB color into console. http://pinvoke.net/default.aspx/kernel32.SetConsoleScreenBufferInfoEx

// Copyright Alex Shvedov
// Modified by MercuryP with color specifications
// Use this code in any way you want

using System;
using System.Diagnostics;                // for Debug
using System.Drawing;                    // for Color (add reference to  System.Drawing.assembly)
using System.Runtime.InteropServices;    // for StructLayout

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

    [StructLayout(LayoutKind.Sequential)]
    internal struct SMALL_RECT
    {
        internal short Left;
        internal short Top;
        internal short Right;
        internal short Bottom;
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct COLORREF
    {
        internal uint ColorDWORD;

        internal COLORREF(Color color)
        {
            ColorDWORD = (uint) color.R + (((uint) color.G) << 8) + (((uint) color.B) << 16);
        }

        internal COLORREF(uint r, uint g, uint b)
        {
            ColorDWORD = r + (g << 8) + (b << 16);
        }

        internal Color GetColor()
        {
            return Color.FromArgb((int) (0x000000FFU & ColorDWORD),
                                  (int) (0x0000FF00U & ColorDWORD) >> 8, (int) (0x00FF0000U & ColorDWORD) >> 16);
        }

        internal void SetColor(Color color)
        {
            ColorDWORD = (uint) color.R + (((uint) color.G) << 8) + (((uint) color.B) << 16);
        }
    }

    [StructLayout(LayoutKind.Sequential)]
    internal struct CONSOLE_SCREEN_BUFFER_INFO_EX
    {
        internal int cbSize;
        internal COORD dwSize;
        internal COORD dwCursorPosition;
        internal ushort wAttributes;
        internal SMALL_RECT srWindow;
        internal COORD dwMaximumWindowSize;
        internal ushort wPopupAttributes;
        internal bool bFullscreenSupported;
        internal COLORREF black;
        internal COLORREF darkBlue;
        internal COLORREF darkGreen;
        internal COLORREF darkCyan;
        internal COLORREF darkRed;
        internal COLORREF darkMagenta;
        internal COLORREF darkYellow;
        internal COLORREF gray;
        internal COLORREF darkGray;
        internal COLORREF blue;
        internal COLORREF green;
        internal COLORREF cyan;
        internal COLORREF red;
        internal COLORREF magenta;
        internal COLORREF yellow;
        internal COLORREF white;
    }

    const int STD_OUTPUT_HANDLE = -11;                                        // per WinBase.h
    internal static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);    // per WinBase.h

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

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool GetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO_EX csbe);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool SetConsoleScreenBufferInfoEx(IntPtr hConsoleOutput, ref CONSOLE_SCREEN_BUFFER_INFO_EX csbe);

    // Set a specific console color to an RGB color
    // The default console colors used are gray (foreground) and black (background)
    public static int SetColor(ConsoleColor consoleColor, Color targetColor)
    {
        return SetColor(consoleColor, targetColor.R, targetColor.G, targetColor.B);
    }

    public static int SetColor(ConsoleColor color, uint r, uint g, uint b)
    {
        CONSOLE_SCREEN_BUFFER_INFO_EX csbe = new CONSOLE_SCREEN_BUFFER_INFO_EX();
        csbe.cbSize = (int)Marshal.SizeOf(csbe);                    // 96 = 0x60
        IntPtr hConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);    // 7
        if (hConsoleOutput == INVALID_HANDLE_VALUE)
        {
            return Marshal.GetLastWin32Error();
        }
        bool brc = GetConsoleScreenBufferInfoEx(hConsoleOutput, ref csbe);
        if (!brc)
        {
            return Marshal.GetLastWin32Error();
        }

        switch (color)
        {
            case ConsoleColor.Black:
                csbe.black = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkBlue:
                csbe.darkBlue = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkGreen:
                csbe.darkGreen = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkCyan:
                csbe.darkCyan = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkRed:
                csbe.darkRed = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkMagenta:
                csbe.darkMagenta = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkYellow:
                csbe.darkYellow = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Gray:
                csbe.gray = new COLORREF(r, g, b);
                break;
            case ConsoleColor.DarkGray:
                csbe.darkGray = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Blue:
                csbe.blue = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Green:
                csbe.green = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Cyan:
                csbe.cyan = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Red:
                csbe.red = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Magenta:
                csbe.magenta = new COLORREF(r, g, b);
                break;
            case ConsoleColor.Yellow:
                csbe.yellow = new COLORREF(r, g, b);
                break;
            case ConsoleColor.White:
                csbe.white = new COLORREF(r, g, b);
                break;
        }
        ++csbe.srWindow.Bottom;
        ++csbe.srWindow.Right;
        brc = SetConsoleScreenBufferInfoEx(hConsoleOutput, ref csbe);
        if (!brc)
        {
            return Marshal.GetLastWin32Error();
        }
        return 0;
    }

    public static int SetScreenColors(Color foregroundColor, Color backgroundColor)
    {
        int irc;
        irc = SetColor(ConsoleColor.Gray, foregroundColor);
        if (irc != 0) return irc;
        irc = SetColor(ConsoleColor.Black, backgroundColor);
        if (irc != 0) return irc;

        return 0;
    }
}

And then if you want to use Orange or any other color you can do a simple call to SetScreenColor

static void Main(string[] args)
    {

        Color screenTextColor = Color.Orange;
        Color screenBackgroundColor = Color.Black;
        int irc = SetScreenColorsApp.SetScreenColors(screenTextColor, screenBackgroundColor);
        Debug.Assert(irc == 0, "SetScreenColors failed, Win32Error code = " + irc + " = 0x" + irc.ToString("x"));

        Debug.WriteLine("LargestWindowHeight=" + Console.LargestWindowHeight + " LargestWindowWidth=" + Console.LargestWindowWidth);
        Debug.WriteLine("BufferHeight=" + Console.BufferHeight + " WindowHeight=" + Console.WindowHeight + " BufferWidth=" + Console.BufferWidth + " WindowWidth=" + Console.WindowWidth);
        //// these are relative to the buffer, not the screen:
        //Debug.WriteLine("WindowTop=" + Console.WindowTop + " WindowLeft=" + Console.WindowLeft);
        Debug.WriteLine("ForegroundColor=" + Console.ForegroundColor + " BackgroundColor=" + Console.BackgroundColor);
        Console.WriteLine("Some text in a console window");
        Console.BackgroundColor = ConsoleColor.Cyan;
        Console.ForegroundColor = ConsoleColor.Yellow;
        Debug.WriteLine("ForegroundColor=" + Console.ForegroundColor + " BackgroundColor=" + Console.BackgroundColor);
        Console.Write("Press ENTER to exit...");
        Console.ReadLine();

        // Note: If you use SetScreenColors, the RGB values of gray and black are changed permanently for the console window.
        // Using i.e. Console.ForegroundColor = ConsoleColor.Gray afterwards will switch the color to whatever you changed gray to

        // It's best to use SetColor for the purpose of choosing the 16 colors you want the console to be able to display, then use
        // Console.BackgroundColor and Console.ForegrondColor to choose among them.
    }
Trenton answered 29/10, 2011 at 5:59 Comment(4)
Yeah I know about those I was hoping there was another way to achieve a custom color. Odd they have DarkMagenta and so on but no orange(our school colors are black and orange) Thanks anyways for the help!Pardo
That the .Net framework doesn't support custom RGB colors for console forecolor is very strange, as it is supported natively, so it seems. Great solution benjamin!Adversary
I've tried to compile this code but I am getting an error: "Additional information: Unable to find an entry point named 'GetConsoleScreenBufferInfoEx' in DLL 'kernel32.dll'." This line seems to be the issue: bool brc = GetConsoleScreenBufferInfoEx(hConsoleOutput, ref csbe);Koetke
"On Vista and later see the SetConsoleScreenBufferInfoEx API function." https://mcmap.net/q/218609/-converting-color-to-consolecolor I'm using Windows XP... Is there any work around for XP?Koetke
D
32

Since Windows 10 Anniversary Update, console can use ANSI/VT100 color codes

  1. You need set flag ENABLE_VIRTUAL_TERMINAL_PROCESSING(0x4) by SetConsoleMode
  2. Use sequences:

    "\x1b[48;5;" + s + "m" - set background color by index in table (0-255)

    "\x1b[38;5;" + s + "m" - set foreground color by index in table (0-255)

    "\x1b[48;2;" + r + ";" + g + ";" + b + "m" - set background by r,g,b values

    "\x1b[38;2;" + r + ";" + g + ";" + b + "m" - set foreground by r,g,b values

Important notice: Internally Windows have only 256 (or 88) colors in table and Windows will used nearest to (r,g,b) value from table.

Sample code:

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        [DllImport( "kernel32.dll", SetLastError = true )]
        public static extern bool SetConsoleMode( IntPtr hConsoleHandle, int mode );
        [DllImport( "kernel32.dll", SetLastError = true )]
        public static extern bool GetConsoleMode( IntPtr handle, out int mode );

        [DllImport( "kernel32.dll", SetLastError = true )]
        public static extern IntPtr GetStdHandle( int handle );

        static void Main( string[] args )
        {
            var handle = GetStdHandle( -11 );
            int mode;
            GetConsoleMode( handle, out mode );
            SetConsoleMode( handle, mode | 0x4 );

            for (int i=0;i<255;i++ )
            {
                Console.Write( "\x1b[48;5;" + i + "m*" );
            }

            Console.ReadLine();
        }
    }
}

Result:

Windows 10 Colors

Read about it in MSDN: Article 'Console Virtual Terminal Sequences'

Dialectics answered 10/4, 2017 at 10:45 Comment(3)
I'm coming back to this old thread to correct something posted above. Using: "\x1b[48;2;" + r + ";" + g + ";" + b + "m" "\x1b[38;2;" + r + ";" + g + ";" + b + "m" absolutely does NOT limit you to 256 colors. I created a console based render engine and when I tested it on a bitmap of my car, the unique color count of a bitmap screen capture was 46,376 unique colors.Deauville
if you use \x1b[48;2; instead of x1b[48;5 you can use RGB(255,255,255) and create a rainbow like https://mcmap.net/q/219845/-ansi-colors-and-writing-directly-to-console-output-cMonodrama
According to doc ``` The 88 and 256 color palettes maintained internally for comparison are based from the xterm terminal emulator. The comparison/rounding tables cannot be modified at this time. ```Dialectics
T
9

Expanding on the answer of Alexei Shcherbakov Windows 10 ENABLE_VIRTUAL_TERMINAL_PROCESSING here is a complete color code mapping so you have all colors and their respective numbers in one place:

enter image description here

Tiro answered 10/2, 2018 at 11:58 Comment(0)
B
3

Better late than never, but it appears this is now possible, on Vista and later at least. So I'll add this for future reference for others with the same question.

When I was looking to do this I came across Hans Passant's reply on MSDN:

I don't have access to Vista right now so can't try it. But something like this ought to work:

CONSOLE_SCREEN_BUFFER_INFOEX info;
info.cbSize = sizeof(info);
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
GetConsoleScreenBufferInfoEx(hConsole, &info);
info.ColorTable[14] = RGB(255, 128, 0);  // Replace yellow
SetConsoleScreenBufferInfoEx(hConsole, &info);
SetConsoleTextAttribute(hConsole, FOREGROUNDINTENSITY | FOREGROUND_RED | FOREGROUND_GREEN);

It will take a little bit of p-Invoking but should give you something to go on with.

Begley answered 25/6, 2012 at 11:19 Comment(1)
thanks, this lead me in the right direction but after having tons of signature matching issues with pinvoke I found the answer over at pinvoke.net/default.aspx/kernel32.SetConsoleScreenBufferInfoExTrenton
S
1

It does not orange because that color is not one of the supported color for console. I mean, you can not get it even using windows API. If you want to verify it, take a look at the following piece of code:

   public static class Win32
    {
        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, short attributes);

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

    public class Program
    {
        static void Main(string[] args)
        {
            foreach(var i in Enumerable.Range(0, 100)) // why "100"? it is just any number
            {
                Win32.SetConsoleTextAttribute(Win32.GetStdHandle(-11), (short)i);
                Console.WriteLine("Hello");
            }
        }
    }
Syncopation answered 29/10, 2011 at 6:35 Comment(2)
my previous answer was deficient as it didn't allow you to overwrite the 16 predefined colors, I found the true answer over at the pinvoke.net and updated my answer below with the wall of code. pinvoke.net/default.aspx/kernel32.SetConsoleScreenBufferInfoExTrenton
The 16 color limit is a throwback to the VGA consoles used in the early years (not the way early years when monochrome was all you got. :) )Meier
W
1

Further proof this does not work (using the approach in Benjamin's link):

using System.Runtime.InteropServices;

namespace
{
    class Program
    {
        [DllImport("kernel32.dll")]
        public static extern bool SetConsoleTextAttribute(IntPtr hConsoleOutput, int wAttributes);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetStdHandle(uint nStdHandle);

        static void Main(string[] args)
        {
            uint STD_OUTPUT_HANDLE = 0xfffffff5;
            IntPtr hConsole = GetStdHandle(STD_OUTPUT_HANDLE);

            SetConsoleTextAttribute(hConsole, (int)Colour.Red + (int)Colour.Green + (int)Colour.Intensity);
            Console.WriteLine("Red + Green + Intensity == Yellow");

            SetConsoleTextAttribute(hConsole, (int)Colour.Red + (int)Colour.Green + (int)Colour.Intensity + (int)Colour.Red);
            Console.WriteLine("Yellow + Red != Orange");

            SetConsoleTextAttribute(hConsole, 15);
            Console.WriteLine();
            Console.WriteLine("Press Enter to exit ...");
            Console.Read();
        }

        public enum Colour
        {
            Blue = 0x00000001,
            Green = 0x00000002,
            Red = 0x00000004,
            Intensity = 0x00000008
        }
    }
}

This method does not allow you to add anything that cannot be reached through ConsoleColor. It's a real shame as I too would love to add orange to my app. If anyone has found a way I would be very interested.

Washedup answered 21/4, 2012 at 4:23 Comment(2)
my previous answer was deficient as it didn't allow you to overwrite the 16 predefined colors, I found the true answer over at the pinvoke.net and updated my answer below with the wall of code. pinvoke.net/default.aspx/kernel32.SetConsoleScreenBufferInfoExTrenton
Your enum Colour is bit masks and should be ORed together, not ADDed together. That's why you can only get the original 16 VGA colors. 4+4=8 (or Red+Red=Intensity).Meier
K
1

a bit late, i know. But one way to achieve colors that are normally not available in C# Console is to change the ColorCode of a color in the Registry. But be warned, this is propably the most unacceptable way to do it. Just open regedit (Win + R, then type "regedit"), go to HKEY_CURRENT_USER, open the key "Console" (At this point you should export the "Console" Key to restore it later). There you will see a list of values from ColorTable00 to ColoTable15. if you change, lets say, ColorTable10 from 0x0000ff00 to 0x0000a5ff (or 65280 to 42495) you will have an orange color when you use ConsoleColor.Green in the Console after restarting it. You can also change this value by code

using System;
using Microsoft.Win32;

namespace colorChanger
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine("Hello World");
            RegistryKey regKey = Registry.CurrentUser.CreateSubKey("Console");
            regKey.SetValue("ColorTable10", 42495, RegistryValueKind.DWord);
            Console.ReadKey();
        }
    }
}

Of course that works with all other ColorTable-Values and Colorcodes but it is changed only for your User on your PC.

Korikorie answered 16/1, 2019 at 10:8 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.