How can I write fast colored output to Console?

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.


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();
        }
    }
}