Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to freopen() both stdout and stderr into a single output file under Windows

I've got a Windows Win32/GUI application that sometimes prints interesting output to both stdout and stderr, so what I'd like to do is capture that output into a file for review after the application has exited.

The problem is, I can successfully call freopen_s() to capture stdout output into a file, or I can use it to capture stderr output into a file, but trying to do both at once yields only a truncated file with munged data.

Below is a simple program that reproduces the problem; if I comment out either one of the two freopen_s() calls, then I get the expected output in the blah.txt file that is created (i.e. one of the two text lines), but what I'd like is to end up with a blah.txt containing both text lines.

Is that possible under Windows? I could fall back to creating two different files (e.g. blah_stdout.txt and blah_stderr.txt) but I'd rather not, since then I'd have to manually reconstruct the relative order in which stdout and stderr output was generated.

int main(int argc, char *argv[])
{
   const char * outFileName = "blah.txt";
   FILE * junk1 = NULL, junk2 = NULL;
   if (freopen_s(&junk1, outFileName, "w", stdout) != 0) abort();
   if (freopen_s(&junk2, outFileName , "w", stderr) != 0) abort();
   printf("This text was printed to stdout and should appear in blah.txt\n");
   fprintf(stderr, "This text was printed to stderr and should appear in blah.txt\n");
   return 0;
}
like image 925
Jeremy Friesner Avatar asked Oct 17 '25 16:10

Jeremy Friesner


1 Answers

The "w" open mode is documented as

Opens an empty file for writing. If the given file exists, its contents are destroyed.

but even "w+" and "a" fail.

If you are using a CRT that has dup2 you can do this:

#include <io.h> // MS CRT

...

FILE * junk1 = NULL;
if (freopen_s(&junk1, outFileName, "w", stdout) != 0) abort();
_dup2(1, 2); // Assign stderr to same file as stdout

This code wrote both lines to the file when I tested it but it is evident from the comments that this might not always be the case. Older versions of Visual Studio are more likely to work.

If you are willing to throw out all portability you might also be able to access the struct behind the FILE objects (struct _iobuf) directly (_iob/__iob_func) and overwrite the members with the values from the other FILE. This is not possible in VS 2015 and later:

FILE Encapsulation: In previous versions, the FILE type was completely defined in <stdio.h>, so it was possible for user code to reach into a FILE and muck with its internals. We have refactored the stdio library to improve encapsulation of the library implementation details. As part of this, FILE as defined in is now an opaque type and its members are inaccessible from outside of the CRT itself.

like image 130
Anders Avatar answered Oct 20 '25 05:10

Anders