Enabling ANSI Escape Codes on Windows 10
In a piece of assessed coursework (ACW) I've done recently, I built a text-based user interface rendering engine. Great fun, but when I went to run it on Windows - all I got was garbage in the console window! I found this strange, since support was announced a year or 2 back. They've even got an extensive documentation page on the subject!
(Above: ANSI escape sequences rendering on Windows. Hooray! Source: This forum post on the IntelĀ® Developer Zone Forums)
The problem lay in the fact that unlike Linux, you actually need to enable it by calling an unmanaged function in the Windows API and flipping an undocumented bit. Sounds terrible? It is.
Thankfully, due to the .NET runtime be it Microsoft's official implementation or Mono handles references to DLLs, it's fairly easy to write a method that flips the appropriate bit in a portable fashion, which I'd like to document in this blog post for future reference.
Firstly, let's setup a method that only executes on Windows. That's easily achieved by checking Environment.OSVersion
:
if(Environment.OSVersion.Platform.ToString().ToLower().Contains("win")) {
IConsoleConfigurer configurer = new WindowsConsoleConfiguerer();
configurer.SetupConsole();
}
Here, we inspect the platform we're running on, and if it contains the substring win
, then we can assume that we're on Windows.
Then, in order to keep the unmanaged code calls as loosely coupled and as far from the main program as possible, I've put bit-flipping code itself in a separate class and referenced it via an interface. This is probably overkill, but at least this way if I run into any further compilation issues it won't be too difficult to refactor it into a separate class library and load it via reflection.
Said interface needs only a single method:
internal interface IConsoleConfigurer
{
void SetupConsole();
}
....I've marked this as internal
, as it's not (currently) going to be used outside the assembly it's located in. If that changes in the future, I can always mark it public
instead.
The implementation of this interface is somewhat more complicated. Here it is:
/// <summary>
/// Configures the console correctly so that it processes ANSI escape sequences.
/// </summary>
internal class WindowsConsoleConfiguerer : IConsoleConfigurer
{
const int STD_OUTPUT_HANDLE = -11;
const uint ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);
[DllImport("kernel32.dll")]
static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode);
[DllImport("kernel32.dll")]
static extern bool SetConsoleMode(IntPtr hConsoleHandle, uint dwMode);
public void SetupConsole() {
IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);
uint mode;
GetConsoleMode(handle, out mode);
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
SetConsoleMode(handle, mode);
}
}
In short, the DllImport
attributes and the extern
keywords tell the .NET runtime that the method should be imported directly from a native DLL - not a .NET assembly.
The SetupConsole()
method, that's defined by the IConsoleConfigurer
interface above, then calls the native methods that we've imported - and because the .NET runtime only imports DLLs when they are first utilised, it compiles and runs just fine on Linux too :D
Found this helpful? Still having issues? Got a better way of doing this? Comment below!