by Kent Reisdorph
You can download sample files from our Web site as part of the file dec98.zip. Visit www.zdjournals.com/cpb and click on the Source Code hyperlink.
Delphi programmers have used the WriteLn function as a debugging tool for a long time. Text passed to WriteLn is sent to a console window, thereby allowing you to use a console window as a real-time log file. By doing so, you can track your program in time-critical sections of code where a breakpoint won't work. C++Builder doesn't include a WriteLn function. It's easy enough, though, to create your own WriteLn function. In this article, we'll explain how to do that. As part of our explanation, we'll examine the AllocConsole, GetStdHandle, and WriteConsole Windows API functions.
AllocConsole();
WriteLn(`Entering Critical Section');
{ Some processing here }
WriteLn(`Exiting Critical Section');
AllocConsole
is a Win32 API function; as its name implies, it allocates a console window for
the application. Any application can have a console window--but it can have
only one. This is true for GUI applications, as well as traditional console
applications. After the console window is allocated, the WriteLn function displays text in the window. The system is simple but extremely effective. In fact, in some debugging situations, nothing else will do.
The good news is that Delphi's WriteLn function can be easily duplicated in C++Builder. In fact, you can go one better by including the AllocConsole call within the WriteLn function. You write text to the console window with the WriteConsole function. This function requires a handle to the output buffer for the console window; you can obtain that handle by calling the GetStdHandle function. The whole thing looks something like this:
AllocConsole(); HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); WriteConsole(handle, "Test", 4, 0, 0);Let's examine this code in more detail. Naturally, the AllocConsole line allocates the console window. In the second line, the GetStdHandle function retrieves a handle to the standard output buffer. You pass STD_OUTPUT_HANDLE to this function to tell Windows to give you the handle of the standard output buffer. Other possible values for this parameter are STD_INPUT_HANDLE and STD_ERROR_HANDLE, but you aren't concerned with those handles for this operation.
Once you have the handle to the standard output buffer, you can call the WriteConsole function to send the text to the console window. The first parameter of WriteConsole is the handle to the standard output buffer, the second parameter is the text to send to the console window, and the third parameter is the number of characters to display. (The fourth parameter returns the number of characters displayed and the fifth parameter is a reserved, unused parameter that's unused; you don't need to worry about either of them here.)
void WriteLn(String text);As you can see, the function takes an AnsiString as a parameter and returns void. Naturally, the text parameter will be used to pass the text you want sent to the console window. Now, let's look at the complete WriteLn function:
void WriteLn(String text)
{
static HANDLE handle;
if (!handle) {
AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
text += "\n";
WriteConsole(handle,
text.c_str(), text.Length(), 0, 0);
}
First,
you declare a static variable called handle; this variable will tell you if the
console window has been allocated already. (Remember, a static variable has an
initial value of 0 and retains its value between function calls.) If handle is
0, then the console window hasn't been created; so, you call AllocConsole.
After that, you call GetStdHandle to obtain the handle to the standard output
buffer. Next, you add a newline character to the end of the string passed in.
Doing so ensures that each line written leaves the cursor in a position to
start a new line. Finally, you call WriteConsole to write the text to the
console window. Notice that you pass the length of the AnsiString as the third
parameter of the WriteConsole function, so that all characters in the string
are written to the console window.
Listing A: WRITELNU.CPP
//--------------------------------------------
#include <vcl\vcl.h>
#pragma hdrstop
#include "WriteLnU.h"
//--------------------------------------------
#pragma resource "*.dfm"
TForm1 *Form1;
// The WriteLn function.
void WriteLn(String text)
{
static HANDLE handle;
if (!handle) {
AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
}
text += "\n";
WriteConsole(handle,
text.c_str(), text.Length(), 0, 0);
}
//--------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//--------------------------------------------
void __fastcall
TForm1::Button1Click(TObject *Sender)
{
for (int i=0;i<20;i++) {
Application->ProcessMessages();
WriteLn("Iteration: " + String(i));
Sleep(100);
}
}