Better text console for C#

By | 2015.08.02

I recently discovered how slow Console.MoveBufferArea actually is. I used it for writing NLog output to a console and discovered that at times of high output it became dead slow. I could at times see it flicker and update line for line while the whole machine temporarily came to a crawl.

As I searched the web I found that this problem is common, but I could not find any suitable solution to it. So I made one myself. I found some info on how to access the Win32 console API in this SO question, as well as the ColorConsoleAppender source code for log4net.

If you are only looking to implement a faster Console app then read my results. This code may or may not be for you.

Output box

While implementing use of WriteConsoleOutput  I added some enhancements over the standard Console.WriteLine() functionality. Basically you define boxes. Each box will scroll individually of the others. So you can have two boxes side-by-side scrolling individual content, or you can have a small box or you can have a single status line separate from the scrolling screen. It writes to the window buffer so that the console scroll bars will work as expected (if size of windows is less than that of the buffer).

Input box

I also needed an input-box. And turns out that the normal Console.ReadLine() easily looses its content when you repaint the screen. Even if I bypassed this by reading the backbuffer content and retaining this over updates it is a standard multiline edit, and if you are on the last line of the window strange stuff happens (scrolls the whole content).

So I implemented my own little ReadLine() function that supports left-arrow, right-arrow, backspace, delete, typing letters and pressing enter. (Should probably have added home/end and stuff too.)

Taking it further

It would be easy to take the source code further to support foreground and background coloring of individual letters.

Result

I took care to implement this whole thing so it doesn’t use too much CPU. Now running a test reveals some surprising results:

Description Time for 10 000 calls
Normal Console.WriteLine 0,2599168 seconds
Console.MoveBuffer + WriteLine ++ 23,4431487 seconds
InputConsoleBox with AutoDraw=true 6,9907394 seconds
InputConsoleBox with AutoDraw=false and one call to Draw() at the end 0,017288 seconds

The bad parts

After a quick profiling session it turns out that calling WriteConsoleOutput is dead slow taking 99% CPU-time on the “AutoDraw=true” test. And from what I can find through searching the web there just isn’t any way around this.

If you implement your own redraw function (calling box.Draw()) and get a 10x speed increase out of console writing and a whopping 1356x speed increase over MoveBuffer. If you imeplement your own redraw then note that Windows does the redraw asynchronously (even after being so slow), so you may want to implement double buffering to avoid flickering (hint: you can create two boxes and just copy each others buffers upon swap).

The good parts

There are some clear advantages on using this implementation:

  • I can control what to scroll.
  • I can have a status-line unaffected by the scrolling.
  • I can have a ReadLine() at the last line of the screen that is unaffected by the scrolling.
  • Flickering is much less than with MoveBuffer.
  • Implementing my own timer does give me a 1356x speed increase over MoveBuffer.

Sourcecode

Example usage

 

The implementation

 

2 thoughts on “Better text console for C#

  1. blarg harblarg

    would be nice if you included a license for using the code :p

    (Apart from the coding convention used,) This is a pretty damn spiffy little bit of code and something I would really love to use this for my next whack at doing a rogue like game :)

    i mean who doesn’t love that CGA text mode graphic charm… and with half block character you can even render out low res bitmaps :p (one half + fore and back colors to represent every 2 pixels vertical pixels)

    Reply
    1. tedd Post author

      Consider it BSD licensed. I should probably put up a default “unless otherwise stated”-license on my blog. :)

      Reply

Leave a Reply