Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
363 views
in Technique[技术] by (71.8m points)

c++ - Redirecting stdout in win32 does not redirect stdout

I'm trying to redirect stdout so that printf in a windows application will go to a file of my choice.

I'm doing this:

outFile = fopen("log.txt", "w");
*stdout = *outFile;
setvbuf(stdout, NULL, _IONBF, 0);

but printf still writes to the console (or nowhere on a GUI based win32 application)

I can redirect 'std::cout' by doing this:

outFileStr = std::ofstream(outFile);         
std::cout.rdbuf(outFileStr.rdbuf());

But printf seems to be doing its own thing. Unfortunately, I need to redirect printf as I'm trying to integrate python in to a C++ framework and that appears to rely on printf rather than std::cout.

std::cout' seems to be redirected but not printf.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Redirecting standard I/O is a bit more involved on Windows due to the lack of a proper interface to map between arbitrary Win32 file handles and higher layer file descriptors/streams.

There are actually three different layers of I/O on Windows:

  1. Standard? C I/O streams (these are used by printf, scanf, ...)
  2. POSIX I/O descriptors (these are integers used by read, write, ...)
  3. Win32 API I/O handles (used by ReadFile, WriteFile, ...)

Redirecting C Streams

To redirect C streams you can use freopen. For instance, you can redirect C stdout using:

freopen("log.txt", "w", stdout);

This redirection will generally not redirect I/O done by POSIX or Win32 APIs (they would still read/write the attached Console, if any). In addition, this redirection will not be inherited by child processes. (On POSIX-compliant/non-Windows systems, it is typical that the POSIX API is also the system API and the C API is implemented on top of the POSIX API. In these cases, freopen is sufficient.)

Redirecting POSIX I/O descriptors

To redirect I/O at the POSIX API level, you can use dup2. For example, you can reassign file descriptor STDOUT_FILENO to redirect stdout, something like:

int fd = open("log.txt", O_WRONLY);
dup2(fd, STDOUT_FILENO);
close(fd);

Unfortunately on Windows, even redirection at the POSIX API level does not guarantee redirection neither at the C nor the Win32 API levels. Whether this will work or not depends on the effort put in the implementation of the C library to map between POSIX file descriptors and Win32 file handles (presuming the C runtime library you are using layered its I/O on top of POSIX to begin with). There is also no guarantee that this redirection will be inherited by spawned children.

Redirecting Std I/O On Windows!

To correctly redirect I/O on Windows you have to redirect at the lowest level (i.e., the Win32 API level) and fix the linkage at higher levels as follows:

  1. Allocate a new handle by calling CreateFile.
  2. Assign that new handle to the desired std I/O device using SetStdHandle.
  3. Associate that new handle with the corresponding C std file descriptor using _open_osfhandle (returns a file descriptor number).
  4. Redirect the returned file descriptor using the dup2 technique explained above.

Here is a sample snippet to redirect stdout:

HANDLE new_stdout = CreateFileA("log.txt", ...);
SetStdHandle(STD_OUTPUT_HANDLE, new_stdout);
int fd = _open_osfhandle(new_stdout, O_WRONLY|O_TEXT);
dup2(fd, STDOUT_FILENO);
close(fd);

P.S. If you can do without I/O redirection inside the program, then you can simply use the Console's I/O redirection at the command line using a tiny Batch file:

@echo off
start "my_gui_app" "path/to/my_gui_app.exe" 1> "path/to/log.txt"

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...