The MFC Console Framework

Overview

gratuitous screen shot

The MFC Console Framework lets you write GUI programs that are almost as quick-and-dirty as console mode programs. Output goes to a scrolling listbox, something like a console mode window. Input comes from menu items and dialog boxes.

This framework isn't very useful for building end-user programs. I use it at work to make quick test programs, or test harnesses for libraries I write. The output window lets me write out debugging messages without having to toggle back to Visual Studio's Debug pane and look for ODS strings among all the other stuff in the Debug area.

Status Reporter

The framework comes with my Status Reporter library. Status Reporter is like Visual C++'s TRACE facility on steroids. It has several advantages over TRACE:

The last point is key. Take, for example, the way the 5 output levels are handled. Your program is in complete control of where the "output area" is, whether a message gets sent there or is ignored, whether or not to use a log file to back up the Windows OutputDebugString() mechanism, etc. Each program derives a special status reporter to handle these local issues. Plus, because the Windows functionality is in a separate subclass, you can use this library on non-Windows platforms by deriving from the base class. We do this on our Linux-based server programs at work.

What is an "output area"? It depends on your program. For many modern GUI programs, it's a status bar. For the MFC Console Framework, it's the main UI's list box. For a Unix server program, it might be stdout, stderr, or the syslog() function, depending on the message's output level. It's completely up to you where to send output messages.

Multiple output levels is very nice. It lets your program have certain debug messages in it that normally the user never sees. Then if you need to get some debug info from a customer in the field, you can have them pass a command line option or something that turns on a higher debug level.

The "verbose status" level is handled separately, so you can put potentially embarrassing messages in your program without giving away the secret to trigger them to just anyone. (E.g. "Oooops, module FROBOZZ has a bug at line 42") Sure, it's preferrable not to have embarrassing messages in the program, but sometimes you just have to have them, like in default cases in C switch statements. Those bits of code should never be executed, but pretending they won't ever be is just naïve.

Using the MFC Console Framework

Nothing could be simpler: copy the project, and start hacking on it!

Using the Status Reporter Separately

The MFC Console Framework is set up to build the Status Reporter as a separate library. Just copy that library and the files StatusReporter.h and WindowsStatusReporter.h to another directory.

In the project that uses the Status Reporter, you need to derive a subclass, probably of WindowsStatusReporter. Look in ConsoleStatusReporter.* for the code to the MFC Console Framework's version: you can probably copy this class, rename it, and have it working for your particular setup with very little effort. The most common change is to have it send messages to your particular program's output area. Other things you might consider are keeping a message log so the user can view past messages.

Somewhere early on in your application's initialization, you need to call MyStatusReporter::GetInstance() to create the global MyStatusReporter instance. The file that calls that function the first time will need to #include MyStatusReporter.h; all other modules can just #include StatusReporter.h, because once the global MyStatusReporter instance is created, you don't need to refer directly to the leaf class ever again. This makes it possible to make major changes to that leaf class without modifying all the code that uses the Status Reporter mechanism.

Sending messages through the Status Reporter mechanism works almost exactly like C++ stream I/O, except that you have to say what level of output you want. (Kind of like the difference between cout and cerr.) Some examples:

	int n = 42;
	REPORT_NORMAL_STATUS("n is " << n);
	if (n != 42) {
		REPORT_PROBLEM("Odd, n isn't 42.");
	}
	else if (n / 3 == 13) {
		REPORT_FATAL_ERROR("Uh, oh, mathematics is breaking down...");
	}
	
	if (n % 7 == 0) {
		REPORT_INTERNAL_STATUS("n is evenly divisible by 7");
	}
	else {
		complex c(n);
		REPORT_VERBOSE_STATUS("Just a streams test: " << c);
	}
	REPORT_STATUS(n);

The meaning of the last line is not obvious. The REPORT_STATUS macro lets you look up status strings by some number, such as a string ID in a Windows STRINGTAGBLE.

The Main Code Path

The Status Reporter mechanism is a little bit complex. This is because I've been using this mechanism for many years now, so it has evolved to handle quite a few real-world situations that aren't obvious initially. To explain how this library works, let's trace the "lookup status message by number" path: it happens to take us past most all the important bits of functionality.

There's one other macro you can use with Status Reporter: END_PROGRAM(). This is basically just a REPORT_FATAL_ERROR() with no message: the mechanism understands that this just means "end the program without saying anything about it." It's up to your Status Reporter subclass to define what "ending the program" means, though.

Downloading

There is a zip file containing a Visual C++ 5.0 project here for you to download. It is intended that you copy this project and write your new code on top of it, but of course you could also pull bits and pieces of it into an existing project.

License

This mechanism is under a BSD-like license. That means you are basically free to do anything you want with the code, as long as you agree not to sue me or my employer. :) The full text of the license is here.


Updated Fri Dec 16 2022 12:23 MST   Go to my home page