How to Write Fast Colored Output to Console

How can I write fast colored output to Console?

Update: added a sample

If you are prepared to do some P/Invoke stuff, this might help.

Basically if you get a handle to the console buffer, then you can use the standard Win32 APIs wot manipulate the buffer, even build the the entire buffer off screen and the blit it to the Console.

The only tricky part is getting the handle to the console buffer. I have not tried this in .NET, but in years gone by, you could get the handle to the current console by using CreateFile (you will need to P/Invoke this) and open "CONOUT$" then you can use the handle that is return to pass to the other APIs.

P/Invoke for CreateFile

http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html

And you can use WriteConsoleOutput to move all the characters and their attributes from a memory buffer to the console buffer.

http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx

You could probably put together a nice library to provide lower-level access to the console buffer.

Since I am trying to get my .NET up to scratch again I thought I would try my hand at this and see if I could get it to work. Here is a sample that will fill the screen with all the letters A-Z and run through all the forground attributes 0-15. I think you will be impressed with the performance. I'll be honest, I did not spend much time reviewing this code so error checking is zero and there might be a little bug here or there but it should get you going with the rest of the APIs.

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace ConsoleApplication1
{
class Program
{

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);

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

public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};

[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public ushort UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}

[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}

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

[STAThread]
static void Main(string[] args)
{
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[80 * 25];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };

for (byte character = 65; character < 65 + 26; ++character)
{
for (short attribute = 0; attribute < 15; ++attribute)
{
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = attribute;
buf[i].Char.AsciiChar = character;
}

bool b = WriteConsoleOutputW(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
}
}
}
Console.ReadKey();
}
}
}

Unicode example

using Microsoft.Win32.SafeHandles;
using System;
using System.IO;
using System.Runtime.InteropServices;

namespace FastConsole
{
class Program
{

[DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern SafeFileHandle CreateFile(
string fileName,
[MarshalAs(UnmanagedType.U4)] uint fileAccess,
[MarshalAs(UnmanagedType.U4)] uint fileShare,
IntPtr securityAttributes,
[MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
[MarshalAs(UnmanagedType.U4)] int flags,
IntPtr template);

[DllImport("kernel32.dll", SetLastError = true)]
static extern bool WriteConsoleOutputW(
SafeFileHandle hConsoleOutput,
CharInfo[] lpBuffer,
Coord dwBufferSize,
Coord dwBufferCoord,
ref SmallRect lpWriteRegion);

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

public Coord(short X, short Y)
{
this.X = X;
this.Y = Y;
}
};

[StructLayout(LayoutKind.Explicit)]
public struct CharUnion
{
[FieldOffset(0)] public ushort UnicodeChar;
[FieldOffset(0)] public byte AsciiChar;
}

[StructLayout(LayoutKind.Explicit)]
public struct CharInfo
{
[FieldOffset(0)] public CharUnion Char;
[FieldOffset(2)] public short Attributes;
}

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

[STAThread]
static void Main(string[] args)
{
SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

if (!h.IsInvalid)
{
CharInfo[] buf = new CharInfo[80 * 25];
SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };

for (ushort character = 0x2551; character < 0x2551 + 26; ++character)
{
for (short attribute = 0; attribute < 15; ++attribute)
{
for (int i = 0; i < buf.Length; ++i)
{
buf[i].Attributes = attribute;
buf[i].Char.UnicodeChar = character;
}

bool b = WriteConsoleOutputW(h, buf,
new Coord() { X = 80, Y = 25 },
new Coord() { X = 0, Y = 0 },
ref rect);
Console.ReadKey();
}
}
}
Console.ReadKey();
}
}
}

Any faster way to set console colors?

Not really. As you've already noted, the properties built in to Console interact with SetConsoleTextAttribute directly, which is the way to set console output properties in Windows.


If ANSI colors are an option, which would be substantial changes to logic you have working with console colors, it is faster. A minimal case

for (int i = 0; i <= 20000; i++)
{
Console.WriteLine("\x1b[31m\x1b[44mHello World\x1b[39m\x1b[49m");
}

runs in about 1200 ms on my machine, compared to about 7000 ms with the Console.xxxColor properties.

You'll need a compatible shell. Ansicon works.


With ANSI colors, you can additionally batch your output for even more times savings. Even batches of lines of 20 chop the run time above in half.* If you're spamming to the console for whatever reason, that could help significantly.

*On my machine.

C# Something faster than Console.Write()?

There's basically two approaches: render less, and render faster.

Render less is usually more tricky, but also tends to be less intensive. The classic example would be Carmack's Keen games - the PC didn't have the guts to rerender the whole screen at once, so Carmack made sure only the parts of the screen that actually change get redrawn. In your case, this can be as simple as checking the new screen against the old screen (without using the Console methods, of course) - depending on the kind of game you're writing, this can save you a huge amount of work.

Render faster is usually easier. The usual approach in the olden days was to get direct access to the output buffer - instead of having the playfield in separate memory, you had it directly in the graphics card - which was quite capable of redrawing the entire screen as fast as needed, of course, since otherwise you wouldn't ever see much on your CRT screen. This option is still accessible as backward compatibility, so you can still use it if you code your application in, say, Turbo Pascal, but it's not really all that easily accessible in C#. There is an option in rendering the whole screen in a StringBuilder first, and then Console.Write that all at once. It's going to be quite a bit faster, but it's not exactly stellar. char[] will allow you an extra point of performance - you can represent your playfield directly as char[][] and then you don't have to recreate the StringBuilder every time you change something - you just have to Console.Write once for each playfield line.

And of course, you could simply write out the changes as soon as they occur; depending on the game you're writing, this can range all the way from "trivial with great results" to "pretty hard and not looking good". And since the buffer area of the console can be bigger than then window size, you could even draw it out to a hidden part of the buffer, and then use Console.MoveBufferArea to draw the whole change at once - this is usually called "backbuffering". I'm not sure if it will look good, though - the console window nowadays allows you to scroll in the buffer, which can be detriminal for your use case.

There's still ways to get much faster access to the console buffers, but not while staying fully in .NET - you'll need to use P/Invokes. A great answer on this topic is here - How can I write fast colored output to Console?. On modern systems, this is pretty much equivalent to using a back buffer and "drawing" it all at once - it's incredibly fast. And again, you can use the back buffer for your game data directly - it worked 20-30 years ago, and it still works today; it's good practice in playing around with limited resources. Can you write a game that only really uses the console text buffer for everything, or at least almost everything? It's pretty fun playing around with stuff like that; you can write a whole plethora of games like this, including games like Tetris or Lode Runner. Of course, this will only work on Windows, so if you want to support other systems, it's a lot trickier.

And finally, you can just write your own console (or better, use someone's already written and tested). It's a good practice if you want to go on to greater challenges over time, and it will allow you to play around with more powerful technologies over time. The typical example would be games like Dwarf Fortress - still text based, still console-like, but actually drawn graphically, using technologies like SDL. Not only is this vastly faster on modern systems (since you have no easy way to access the text buffers directly), it also opens up the option of changing over to graphical tiled game rather easily. It's yet another stepping stone on the stairway to cool stuff :))

How to color words in different colours in a Console.WriteLine in a console application?

If you wanna make different color to each text on console you should write Console.BackgrundColor and Console.ForeGroundColor before each input and output in consolse. For example:

        Console.BackgroundColor = ConsoleColor.Yellow;
Console.ForegroundColor = ConsoleColor.Red;

Console.WriteLine("Enter your name:");

string name = Console.ReadLine();

Console.BackgroundColor = ConsoleColor.Green;
Console.ForegroundColor = ConsoleColor.Yellow;

Console.WriteLine("Hello, " + name);

Console.ReadKey();

Colorizing text in the console with C++

Add a little Color to your Console Text

  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
// you can loop k higher to see more color choices
for(int k = 1; k < 255; k++)
{
// pick the colorattribute k you want
SetConsoleTextAttribute(hConsole, k);
cout << k << " I want to be nice today!" << endl;
}

alt text

Character Attributes
Here is how the "k" value be interpreted.

Capture coloured console output from multiple sources

I was on the right track with CreateConsoleScreenBuffer and giving each thread its own console screen buffer.

The problem was ReadConsole which doesn't do what I expected.

I now got it working with ReadConsoleOutput.

It should be noted however, that this method is the legacy way of doing it.
If you want to do it the "new way" you should probably use Pseudo Console Sessions.

Its support starts with Windows 10 1809 and Windows Server 2019.

It should also be noted, that the method of reading the output of a process/program via console screen buffer has its flaws and two distinct disadvantages compared to anonymous pipes:

  1. The console screen buffer can't get full and block the process/program, but if the end of it is reached, new lines will push the current first line out of the buffer.
  2. Output from processes/programs that spam their std output in a fast fashion will most likely lead to loss of information, as you won't be able to read, clear and move the cursor in the console screen buffer fast enough.

I try to circumvent both by increasing the console screen buffers y size component to its maximum possible size (I found it to be MAXSHORT - 1) and just wait until the process/program has finished.

That's good enough for me, as I don't need to analyze or process the colored output, but just display it in a console window, which is itself limited to MAXSHORT - 1 lines.

In every other scenario I will be using pipes and advise everyone else to do so too!

Here is a short version without any error handling that can be executed in parallel without interference (provided the TStream object is owned by the thread or thread-safe):

procedure CreateProcessWithConsoleCapture(const aAppName, aCMD, aDefaultDir: PChar;
const CapturedOutput: TStream);
const
CONSOLE_SCREEN_BUFFER_SIZE_Y = MAXSHORT - 1;
var
SecurityAttributes: TSecurityAttributes;
ConsoleHandle: THandle;
StartupInfo: TStartupInfo;
ProcessInformation: TProcessInformation;
CharsRead: Cardinal;
BufferSize, Origin: TCoord;
ConsoleScreenBufferInfo: TConsoleScreenBufferInfo;
Buffer: array of TCharInfo;
ReadRec: TSmallRect;
begin
SecurityAttributes.nLength := SizeOf(SecurityAttributes);
SecurityAttributes.lpSecurityDescriptor := Nil;
SecurityAttributes.bInheritHandle := True;

ConsoleHandle := CreateConsoleScreenBuffer(
GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE,
@SecurityAttributes,
CONSOLE_TEXTMODE_BUFFER,
nil);

try
GetConsoleScreenBufferInfo(ConsoleHandle, ConsoleScreenBufferInfo);
BufferSize.X := ConsoleScreenBufferInfo.dwSize.X;
BufferSize.Y := CONSOLE_SCREEN_BUFFER_SIZE_Y;
SetConsoleScreenBufferSize(ConsoleHandle, BufferSize);

Origin.X := 0;
Origin.Y := 0;
FillConsoleOutputCharacter(ConsoleHandle, #0, BufferSize.X * BufferSize.Y, Origin, CharsRead);

SetStdHandle(STD_OUTPUT_HANDLE, ConsoleHandle);

FillChar(StartupInfo, Sizeof(StartupInfo), 0);
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.hStdOutput := ConsoleHandle;
StartupInfo.hStdError := ConsoleHandle;
StartupInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
StartupInfo.dwFlags := STARTF_USESTDHANDLES or STARTF_FORCEOFFFEEDBACK;

CreateProcess(aAppName, aCMD,
@SecurityAttributes, @SecurityAttributes,
True, NORMAL_PRIORITY_CLASS,
nil, aDefaultDir,
StartupInfo, ProcessInformation);

try
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);

GetConsoleScreenBufferInfo(ConsoleHandle, ConsoleScreenBufferInfo);

BufferSize.X := ConsoleScreenBufferInfo.dwSize.X;
BufferSize.Y := ConsoleScreenBufferInfo.dwCursorPosition.Y;

if ConsoleScreenBufferInfo.dwCursorPosition.X > 0 then
begin
Inc(BufferSize.Y);
end;

ReadRec.Left := 0;
ReadRec.Top := 0;
ReadRec.Right := BufferSize.X - 1;
ReadRec.Bottom := BufferSize.Y - 1;

SetLength(Buffer, BufferSize.X * BufferSize.Y);
ReadConsoleOutput(ConsoleHandle, @Buffer[0], BufferSize, Origin, ReadRec);

CharsRead := SizeOf(TCharInfo) * (ReadRec.Right - ReadRec.Left + 1) * (ReadRec.Bottom - ReadRec.Top + 1);
if CharsRead > 0 then
begin
CapturedOutput.Size := CapturedOutput.Size + CharsRead;
CapturedOutput.WriteBuffer(Buffer[0], CharsRead);
end;

finally
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
end;
finally
CloseHandle(ConsoleHandle);
end;
end;

Change Console text Color in C# after writing

You can change the console output color using Console.BackgroundColor and Console.ForegroundColor properties. After you are done writing in the new color, use Console.ResetColor() to go back to defaults.

Changing the colors after the fact is a problem, because C# has no direct way to read text at a given position.You can rewrite it however, if you know what exactly is there, in a different color (first jumping to the location using Console.SetCursorPosition method and then writing over the original text).

If you want to be as efficient as possible, you will need a higher caliber in form of some P/Invoke wizadry. This is quite well described in the accepted answer to this similar question. The solution there takes advantage of writing the entire Console buffer at once, which is very fast.



Related Topics



Leave a reply



Submit