How can I write fast colored output to Console?
I want to learn if there is another (faster) way to output text to the console application window using C# .net than with the simple Write, BackgroundColor and ForegroundColor methods and properties? I learned that each cell has a background color and a foreground color, and I would like to cache/buffer/write faster than using the mentioned methods.
Maybe there is some help using the Out buffer, but I don't know how to encode the colors into the stream, if that is where the color data resides.
This is for a retrostyle textbased game I am wanting to implement where I make use of the standard colors and ascii characters for laying out the game.
Please help :)
Update:
The Out and buffer is probably not what I need to mess around with. There seems to be a screen buffer that is owned by the console. I don't know how to access it, maybe I am just out of luck unless I import some dlls.
Solution 1:
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();
}
}
}
Solution 2:
If you look at the implementation of Console
's properties for altering console colours, they delegate to the SetConsoleTextAttribute method from kernel32.dll
. This method takes character attributes as input to set both the foreground and background colours.
From several MSDN doc pages, each screen buffer (of which a console has one) has a two-dimensional array of character info records, each represented by a CHAR_INFO. This is what determines the colour of each character. You can manipulate this using the SetConsoleTextAttribute
method, but this is applied to any new text that is written to the console - you cannot manipulate existing text already on the console.
Unless there is a lower-level hook into the console text colour properties (which doesn't look likely), I think you are stuck using these methods.
One thing you could try is to create a new screen buffer, write to that, and then switch it to be the console's current buffer using SetConsoleActiveScreenBuffer. This may yield faster output as you will be writing all output to an inactive buffer.
Solution 3:
I had success using
using (var stdout = Console.OpenStandardOutput(Cols * Rows))
{
// fill
stdout.Write(buffer, 0, buffer.Length);
// rinse and repeat
}
But if anyone can advise me on how I write extended ASCII to this i'd be grateful