Introduction
Sometimes it is useful and necessary to redirect the standard streams for output and error output.
By default they are printed on the console screen but by redirection, it is possible to capture the
printed text in a e.g. log file. This guide shows one way of doing this.
A Simple Redirection Example
We will implement a console application that redirects the stdout and stderr to a log file.
The code that does the actual starting and stopping of the redirection is put in a separate class.
Note that this is only tested on MS Windows XP.
Main method
The listing below consists of the redirecting class and the main method.
The main method is very straightforward: first it creates an object of SimpleRedirect and
then it makes a call to the StartRedirect method. This causes the output to be saved in a custom log file.
We now make a couple of system calls, e.g. system("dir") and system("silly").
The "silly" command is obviously invalid and will therefore be printed in the log
file by the stderr stream redirection while the valid "dir" will be printed by the stdout. This is exactly
what we wanted in this guide!
The call to EndRedirect will "switch off" the log file output and redirect stdout and stderr
back to printing on the console screen.
By using the class to hide the implementation of the redirection, it is possible to keep a
clean and simple code that focus on the main tasks. Next we investigate the SimpleRedirect class.
#include <tchar.h>
#include <string>
#include <iostream>
#include "SimpleRedirect.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
const string KFileLocation = "C:\\Temp\\output.txt";
SimpleRedirect* redirect = SimpleRedirect::New();
if (redirect != NULL)
{
redirect->StartRedirect(KFileLocation);
system("dir");
system("silly");
redirect->EndRedirect();
system("echo to console");
system("no-good");
delete redirect;
}
return 0;
}
The SimpleRedirect Class Header
Below is the header file with the class declaration. We have implemented this class
as a singleton, as there is only one stdout and stderr. That way, it is harder to
make mistakes and easier to avoid bugs. This is the reason for declaring the
copy constructor, copy assignment and default constructor as private.
#pragma once
#include <string>
#include <io.h>
using namespace std;
class SimpleRedirect
{
public:
static SimpleRedirect* New();
~SimpleRedirect(void);
bool StartRedirect(string aFileName);
void EndRedirect(void);
private:
SimpleRedirect(void);
SimpleRedirect(const SimpleRedirect&);
SimpleRedirect& operator=(const SimpleRedirect&);
private:
int iOldStdout;
int iOldStderr;
bool iIsRedirecting;
FILE* iFile;
static SimpleRedirect* iInstance;
};
The Source Code
Finally, we have listed the source code for SimpleRedirect. Please read the inline comments
for information about the code. Notice how we have used the C runtime to implement the behaviour.
#include "SimpleRedirect.h"
SimpleRedirect* SimpleRedirect::iInstance = NULL;
SimpleRedirect* SimpleRedirect::New()
{
if (iInstance == NULL)
{
iInstance = new SimpleRedirect();
}
return iInstance;
}
SimpleRedirect::SimpleRedirect(void)
: iOldStdout(0), iOldStderr(0),
iIsRedirecting(false), iFile(NULL)
{
}
SimpleRedirect::~SimpleRedirect(void)
{
EndRedirect();
}
bool SimpleRedirect::StartRedirect(string aFileName)
{
bool result = false;
if (iIsRedirecting)
{
EndRedirect();
}
iFile = _fsopen(aFileName.c_str(), "w+", SH_DENYNO);
if (iFile != NULL)
{
iOldStdout = _dup(1);
iOldStderr = _dup(2);
if (!_dup2(_fileno(iFile), 1) && !_dup2(_fileno(iFile), 2))
{
iIsRedirecting = true;
result = true;
}
}
return result;
}
void SimpleRedirect::EndRedirect()
{
if (iIsRedirecting)
{
_dup2(iOldStdout, 1);
_dup2(iOldStderr, 2);
fclose(iFile);
iIsRedirecting = false;
}
}
|