在win32中重定向stdout不能重定向stdout。

4

我想要重定向标准输出,使得在Windows应用程序中的printf函数可以将输出内容写入到我选择的文件中。

我正在尝试以下方法:

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

但是printf仍然会在控制台上输出(或在基于GUI的win32应用程序中不输出)

我可以通过以下方式重定向'std :: cout':

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

但是printf似乎在做它自己的事情。不幸的是,我需要重定向printf,因为我正在尝试将Python集成到C++框架中,而这似乎依赖于printf而不是std::cout。

'std::cout'似乎已经被重定向了,但printf没有。


3
在C语言中这个问题并不那么简单,但是有一个名为freopen的函数可以帮助你。 - Some programmer dude
1
一个基于GUI的Win32应用程序是否有stdout? - Lightness Races in Orbit
@Someprogrammerdude 谢谢,老兄。我之前不知道这个函数,现在终于把它用好了。 我发现有很多示例都能重写 'stdout',但是却无法正常工作。 - Luther
1
@LightnessRacesinOrbit 如果你重定向std :: cout,那么是的。 我将其多路复用到一个缓冲对象中,该对象写入OutputDebugString,控制台(如果可用)和其他日志。 希望现在我也可以使用基于printf的类似技巧。 - Luther
1
稍微挑剔一下:在C++中,你所做的并不是“重定向”,而是替换了std::cout写入的整个缓冲区。 - Some programmer dude
显示剩余6条评论
1个回答

15

在Windows上重定向标准I/O会更加复杂,因为缺少一个合适的接口来映射任意Win32文件句柄和更高层的文件描述符/流之间的关系。

实际上,在Windows上有三个不同的I/O层:

  1. 标准C I/O流(这些是由printfscanf等使用的)
  2. POSIX I/O描述符(这些是readwrite等使用的整数)
  3. Win32 API I/O句柄(由ReadFileWriteFile等使用)

重定向C流

要重定向C流,您可以使用freopen。例如,您可以使用以下命令重定向C stdout

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

这种重定向通常不会重定向 POSIX 或 Win32 API 的 I/O(如果有的话,它们仍然会读取/写入附加的控制台)。此外,这种重定向不会被子进程继承。(在符合 POSIX/非 Windows系统上,通常 POSIX API 也是系统 API,C API 是在 POSIX API 之上实现的。在这些情况下,freopen 已经足够了。)

重定向 POSIX I/O 描述符

要在 POSIX API 级别上重定向 I/O,可以使用 dup2。例如,您可以重新指定文件描述符 STDOUT_FILENO 来重定向 stdout,类似于:

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

很遗憾,在Windows上,即使在POSIX API级别进行重定向也不能保证在C或Win32 API级别上进行重定向。这是否有效取决于C库实现所投入的工作,以映射POSIX文件描述符和Win32文件句柄(假设使用的C运行时库从一开始就在POSIX之上进行了I/O层)。还不能保证此重定向将被生成的子进程继承。

在Windows上重定向标准I / O!

要在Windows上正确重定向I / O,您必须在最低级别(即Win32 API级别)进行重定向,并在更高级别上修复链接,具体如下:

  1. 通过调用 CreateFile 来分配新句柄。
  2. 使用 SetStdHandle 将该新句柄分配给所需的std I / O设备。
  3. 使用 _open_osfhandle (返回文件描述符号码)将该新句柄与相应的C std文件描述符相关联。
  4. 使用上述解释的dup2 技术重定向返回的文件描述符。

这是一个样本片段以重定向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);

顺便说一句,如果不需要程序内进行输入/输出重定向,那么您可以使用一个小批处理文件在命令行中使用控制台的输入/输出重定向:


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

1
非常重要的一点是,dup2 必须使用直接数字而不是 _fileno(stdout),因为文档说明它可能返回负数,如果标准句柄已关闭(父进程调用了不带标准句柄继承的 CreateProcess),则确实会返回负数。否则,所有序列调用都将失败,这意味着 _close(fd) 将关闭句柄而不进行复制! - Andry

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接