I am attempting to redirect cerr
to a file at the start of a program, then at the end of the program set it back to normal. This is along the lines of the accepted answer of Use cout or cerr to output to console after it has been redirected to file
I am running into strange behavior in C Builder for this. The environment seems to want me to refresh the stream redirection every time the program wants to output something.
I created the following test app to demonstrate the problem. If I comment out the #define
on the first line, I get memory access violations. When the #define
is uncommented, it seems to work fine while the program is executing; however, in a larger program, I believe I have also gotten the same memory access violations after the application terminates (works fine during execution with that #define
in there – I still need to explore that one some more to track it down).
//#define MAKE_WORK
#include <vcl.h>
#pragma hdrstop
#include <fstream>
#include "TestRdbuf.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
std::streambuf* origBuff;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormCreate(TObject *Sender)
{
std::ofstream oFile("ErrorLog.txt");
origBuff = std::cerr.rdbuf();
std::cerr.rdbuf(oFile.rdbuf());
std::cerr << "Started: " << Now() << std::endl;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action)
{
#ifdef MAKE_WORK
std::ofstream oFile("ErrorLog.txt", std::ios_base::app);
std::cerr.rdbuf(oFile.rdbuf());
#endif
std::cerr << "Closing:" << Now() << std::endl;
std::cerr.rdbuf(origBuff);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
#ifdef MAKE_WORK
std::ofstream oFile("ErrorLog.txt", std::ios_base::app);
std::cerr.rdbuf(oFile.rdbuf());
#endif
std::cerr << "Debug log time: " << Now() << std::endl;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnShowFileClick(TObject *Sender)
{
TMemo * Memo = new TMemo(pnlBody);
Memo->Parent = pnlBody;
Memo->Align = TAlign::alClient;
Memo->Font->Size = 12;
Memo->Lines->LoadFromFile("ErrorLog.txt");
Memo->ReadOnly = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
#ifdef MAKE_WORK
std::ofstream oFile("ErrorLog.txt", std::ios_base::app);
std::cerr.rdbuf(oFile.rdbuf());
#endif
std::cerr << "Exit clicked: " << Now() << std::endl;
Application->Terminate();
}
//---------------------------------------------------------------------------
CodePudding user response:
First off, DO NOT use the Form's OnCreate
event in C . It is a Delphi idiom that introduces undefined behavior in C , as it can be triggered before the Form's constructor, so use the actual constructor instead. Likewise, the Form's OnDestroy
event can be triggered after the Form's destructor, so use the actual destructor instead.
Second, more importantly, oFile
is a local variable. You are setting cerr
to share the same streambuf
object as oFile
, but then oFile
goes out of scope afterwards and is destroyed, which also destroys its streambuf
object, leaving cerr
holding a dangling pointer to invalid memory, hence the Access Violations.
You need to keep the ofstream
object alive while cerr
is sharing its streambuf
object, eg:
#include <vcl.h>
#pragma hdrstop
#include <fstream>
#include "TestRdbuf.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
std::ofstream oFile;
std::streambuf* origBuff;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
oFile.open("ErrorLog.txt");
origBuff = std::cerr.rdbuf();
std::cerr.rdbuf(oFile.rdbuf());
std::cerr << "Started: " << Now() << std::endl;
}
//---------------------------------------------------------------------------
__fastcall TForm1::~TForm1()
{
std::cerr << "Closing:" << Now() << std::endl;
std::cerr.rdbuf(origBuff);
oFile.close();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
std::cerr << "Debug log time: " << Now() << std::endl;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::btnShowFileClick(TObject *Sender)
{
TMemo *Memo = new TMemo(pnlBody);
Memo->Parent = pnlBody;
Memo->Align = TAlign::alClient;
Memo->Font->Size = 12;
Memo->Lines->LoadFromFile("ErrorLog.txt");
Memo->ReadOnly = true;
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
std::cerr << "Exit clicked: " << Now() << std::endl;
Application->Terminate();
}
//---------------------------------------------------------------------------