在win32中重定向stdout不会重定向stdout

Redirecting stdout in win32 does not redirect stdout

本文关键字:stdout 重定向 win32      更新时间:2023-10-16

我正在尝试重定向stdout,以便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。

由于缺乏在任意Win32文件句柄和更高层文件描述符/流之间进行映射的适当接口,重定向标准I/O在Windows上更为复杂。

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

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

重定向C流

要重定向C流,可以使用freopen。例如,您可以使用重定向Cstdout

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运行库一开始就将其I/O分层在POSIX之上)。也不能保证此重定向将由派生的子代继承。

重定向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);

p.S.如果您可以在程序中不进行I/O重定向,那么您可以在命令行中使用一个小批量文件简单地使用控制台的I/O重定向:

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