Introduction
This article is the first of a series, which I shall call Console Magic, that covers a number of techniques that I have developed over the last ten years or so to simplify and expedite writing utility programs in C# and VB.NET.
Having spent quite a bit of time in front of a NetWare console and written my share of QuickBASIC utility programs, I knew that it was possible to output text in colors other than the default text and background colors set for the console window in which it runs. While doing so isn't all that hard, I decided to discover what it would take to make the task transparent. Figure 1 (below) shows the MessageInColor
class in action.
Figure 1 is the 1st of 4 screens that use four similar sets of methods to write in color in a console window. This first screen demonstrates the color version of Console.WriteLine
, which I called MessageInColor.RGBWriteLine
, which is static, but requires two extra arguments to specify the foreground and background colors. For the eagle eyes among you, I took this screen capture before I settled on the final naming scheme for the static and instance methods.
The MessageInColor
class is intended for use as a more or less drop-in replacement for the System.Console.Write
and System.Console.WriteLine
methods, with convenience features to simplify displaying messages in something other than the default screen colors. It is intended for adding “spot color,” such as when you need to call attention to something important, such as an error message. Everything this class does can be achieved using built-in properties and methods of the System.Console
class, but it’s a tad more work.
Along the same lines, I wanted my programs to be able to continuously display progress messages without scrolling the text above them, such as the way most command line archiving programs report progress when asked to compress a big file. It was no surprise to me that this was a lot more work. Nevertheless, the result was a small class that is easy to apply to new projects or retrofit to existing ones. The next article in this series explains the FixedConsoleWriter
class.
Future articles will discuss other helper classes for use with console programs that add timed pauses, with and without options to interrupt them, a replacement for the lame "Press any key to continue." message, exception logging, and other features, all exposed by the same handful of class libraries.
Background
The classes presented here take advantage of four long established features of every console of which I know that supports color displays. These features were well established when IBM PC-DOS hit the market in 1981, and were carried forward into Windows from the very first version, and the last two are inherited from the typewriter.
- Every character has a color attribute. Strictly speaking each character has two, a text (foreground) and background color. Although modern displays support 24 bit color, this article confines itself to the original 16 colors defined by the
ConsoleColor
enumeration of the System.Console
class. - The console is a rectangular array of (typically) 80 columns by 25 rows or 132 columns by 43 lines. Like all good arrays, the coordinate system starts at zero. In the interest of clearly documenting the code, I defined
LEFT_EDGE
as an Integer constant with a value of zero. The actual width of the console is a user configurable value, which is tracked by the Console.WindowWidth
property. - An unadorned Carriage Return character behaves exactly like the carriage return of a typewriter. If you paid close attention to the behavior of a typewriter, carriage return and line feed were two separate actions, which you used to your advantage when you wanted to make some or all of a line bold. The same behavior carried over to line printers, and the same tricks were used to make them print bold text.
- The cursor advances to the next position afer each letter is written. So, when you write "hello" on a console, the cursor stops at position 6. Therefore, when you write the 80th character on a line, the text wraps, and you get an implicit line feed, so that the 81st character goes into column 1 of the next line.
These behaviors are not unique to the Console.Write
and Console.WriteLine
methods; the corresponding routines in the C runtime library and the Windows Platform SDK exhibit the same behavior.
Using the code
The accompanying package contains two demonstration programs that, between them, demonstrate every method and class discussed in these articles,.
- Console_Color_Writer\TestStand\bin\Release\TestStand.exe demonstrates the 18 overloads of static methods
MessageInColor.RGBWriteLine()
and MessageInColor.RGBWrite(),
instance methods MessageInColor.WriteLine()
and MessageInColor.Write()
, the ErrorMessageColors
property of the AppExceptionLogger
class, and the FixedConsoleWriter
class. - DLLServices2\DLLServices2TestStand\bin\Release\DLLServices2TestStand.exe is a basic demonstration of the
MessageInColor
class and a thorough treatment of the ErrorMessagesInColor
class, which implements default colors for fatal and nonfatal error messages that can be overridden on a per-application basis.
The MessageInColor class exposes two functionally identical sets of methods, both of which closely follow the static Write
and WriteLine
methods of the System.Console
class.
- MessageInColor instances have
MessageForegroundColor
and MessageBackgroundColor
properties, which store the foreground (text) and background colors that you designate when you call the constructor. You can implemnet a color scheme by defining a set of appropriately named MessageInColor instances. - I named the instance methods
Write
and WriteLine
, to make clear that their signatures are analogous to those of the like named static methods of System.Console. - The corresponding static methods are RGBWriteLine and RGBWrite, to remind you that you must speicify foreground and background colors on each call.
Why Two Identical Classes?
Superficially, MessageInColor and ErrorMessagesInColor are identical. However, on closer examination, you will discover that the latter uses the Console.Error
property to write to the Standard Errror (STDERR
) stream, while MessageInColor writes to Standard Output (STDOUT
) through the Console.Out
property. Writing error messages to the STDERR stream makes them visible even when STDOUT is redirected to a file.
A significant consequence of this behavior is that error messages are not captured in normal redirected output.. Ususally, this is desireable, since the redirected output is intended to capture a report, while you want the error messages to be visible. Take heart, however, because the AppExceptionLogger class can be configured to record errors in the Windows event log of your choosing, which was its original motivation, and additional, but sparsely documented, featurex exist for capturing the Standard Errror stream separately.
What's This About Default Colors?
I believe very strongly that, whenever practical, programs should store values tha might change, ever, as data, to be read at run time. To that end, I established default colors for fatal and recoverable (nonfatal) error messages that are read from a configuration file. Since my intention was for these defaults to be more or less global, I wanted them to accompany the DLL, and be automatically available to any appluication that sets a reference to it.
Figure 2 shows the ErrorMessagesInColor
class in action, using the default colors specified in WizardWrx.DLLServices2.dll.config
,.
The library that hosts the color message writers, WizardWrx.DLLServices2.dll
. has its own configuratin file, WizardWrx.DLLServices2.dll.config
, which it knows how to find, so long as a copy travels with it. To that end, its CopyToOutputDirectory
property is set to Always
in the project configuration file.
The working part of the configuration file is very straightworward.
<configuration>
<appSettings>
<add key="FatalExceptionTextColor" value="White"/>
<add key="FatalExceptionBackgroundColor" value="DarkRed"/>
<add key="RecoverablelExceptionTextColor" value="Black"/>
<add key="RecoverableExceptionBackgroundColor" value="Yellow"/>
</appSettings>
</configuration>
To keep things simple, the colors are stored as string literals that correspond to the string representation of the ConsoleColors
enumeration, and the key names correspond to the property names.
The colors reflect choices that I made after studying many combinations and solicitation of input from computer users regarding legibility. However, they aren't sacred; please feel free to aubstitute your own preferences. You override the default colurs by setting the ErrorMessageColors
property of the AppExceptionLogger
object, as shown below.
s_theApp.BaseStateManager.AppExceptionLogger.ErrorMessageColors = new ErrorMessagesInColor (
ConsoleColor.DarkRed ,
ConsoleColor.White );
Figure 3 shows what happens when the settings shown above are in force when an exception is reported.
Figure 3 shows the ErrrorMessagesInColor
class, using application defined colors.
Points of Interest
The default console colors must be preserved and restored at all times. To that end, the MessageInColor
constructor saves the current console colors into a pair of private ConsoleColor
variables, _clrOrigForeColor
and _clrOrigForeColor
. These are reference values, and are not otherwise used directly, apart from read access through a pair of public properties. They exist to give you a method of restoring the original console colors before your program exits.
The instance writers use another pair, _clrSaveForeColor
and _clrSaveBackColor
, to save and restore the current colors before and after each operation. Hence, an intervening call to Console.WriteLine
or Console.Write
displays text in the current console colors. This feature permits you to freely mix and match the methods and properties of this class and those of System.Console
.
Before it writes anything, every instance method calls SetMessageColors
to save the current console colors, then set the colors defined for the instance.
private void SetMessageColors ( )
{
_clrSaveBackColor = Console.BackgroundColor;
_clrSaveForeColor = Console.ForegroundColor;
Console.BackgroundColor = _clrTextBackColor;
Console.ForegroundColor = _clrTextForeColor;
}
A similar, but simpler, companion method, RestoreMessageColors
, restores the original colors after the writer has finished writing.
private void RestoreMessageColors ( )
{
Console.BackgroundColor = _clrSaveBackColor;
Console.ForegroundColor = _clrSaveForeColor;
}
The WriteLine
methods have one last task to perform, which is to call Console.WriteLine
with an empty argument list to finish the line, as shown in the WriteLine
overload that writes a single string.
public void WriteLine (
string value )
{
SetMessageColors ( );
Console.Write ( value );
RestoreMessageColors ( );
Console.WriteLine ( );
}
The corrsponding Console.Write
method gets the colored text onto the console, and the cursor is advanced by calling Console.WriteLine
immediately after RestoreMessageColors
. These two must happen in this order, or a call to either Console.Write or Console.WriteLine would render at least the next line of text in the wrong colors.
To support appending text with a subsequent method call, the Write methods don't call Console.WriteLine. However, since the default colors are restored, if the next routine that writes to the console is Console.Write or Console.WriteLine, the text is rendered in the expected console colors.
The static methods employ similar techniques to save and restore console colors.
History
Sunday, 10 May 2015 is the release date of this article.
I deliver robust, clean, adaptable, future-ready applications that are properly documented for users and maintainers. I have deep knowledge in multiple technologies and broad familiarity with computer and software technologies of yesterday, today, and tomorrow.
While it isn't perceived as sexy, my focus has always been the back end of the application stack, where data arrives from a multitude of sources, and is converted into reports that express my interpretation of The Fundamental Principle of Tabular Reporting, and are the most visible aspect of the system to senior executives who approve the projects and sign the checks.
While I can design a front end, I prefer to work at the back end, getting data into the system from outside sources, such as other computers, electronic sensors, and so forth, and getting it out of the system, as reports to IDENTIFY and SOLVE problems.
When presented with a problem, I focus on identifying and solving the root problem for the long term.
Specialties: Design: Relational data base design, focusing on reporting; organization and presentation of large document collections such as MSDS libraries
Development: Powerful, imaginative utility programs and scripts for automated systems management and maintenance
Industries: Property management, Employee Health and Safety, Services
Languages: C#, C++, C, Python, VBA, Visual Basic, Perl, WinBatch, SQL, XML, HTML, Javascript
Outside Interests: Great music (mostly, but by no means limited to, classical), viewing and photographing sunsets and clouds, traveling by car on small country roads, attending museum exhibits (fine art, history, science, technology), long walks, especially where there is little or no motor traffic, reading, especially nonfiction and thoughtfully written, thought provoking science fiction