在win32中重定向标准输出不会重定向标准输出
Posted
技术标签:
【中文标题】在win32中重定向标准输出不会重定向标准输出【英文标题】:Redirecting stdout in win32 does not redirect stdout 【发布时间】:2019-01-08 14:40:42 【问题描述】:我正在尝试重定向标准输出,以便 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。
【问题讨论】:
C语言没那么简单,但是有freopen
函数可以帮到你。
基于 GUI 的 Win32 应用程序是否甚至有标准输出?
@Someprogrammerdude 谢谢老兄,我不知道这个功能,完成了这项工作。我遇到了很多只是覆盖了“stdout”但没有用的例子。
@LightnessRacesinOrbit 如果你重定向 std::cout 那么是的。我这样做是为了将 std::cout 多路复用到写入 OutputDebugString、控制台(如果可用)和其他日志的缓冲区对象。希望现在我可以用基于 printf 的东西做类似的技巧。
轻微吹毛求疵:您在 C++ 中所做的并不是真正的“重定向”,而是替换了 std::cout
写入的整个缓冲区。
【参考方案1】:
重定向标准 I/O 在 Windows 上涉及更多,因为缺乏适当的接口来映射任意 Win32 文件句柄和更高层文件描述符/流。
Windows 上实际上存在三个不同的 I/O 层:
-
标准? C I/O 流(这些被
printf
、scanf
、...使用)
POSIX I/O 描述符(这些是read
、write
、...使用的整数)
Win32 API I/O 句柄(由ReadFile
、WriteFile
、...使用)
重定向 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 运行时库将其 I/O 分层在 POSIX 之上开始) .也不能保证这个重定向会被派生的孩子继承。
在 Windows 上重定向标准 I/O!
要在 Windows 上正确重定向 I/O,您必须在最低级别(即 Win32 API 级别)重定向并在更高级别修复链接,如下所示:
-
通过调用
CreateFile
分配一个新句柄。
使用 SetStdHandle
将该新句柄分配给所需的 std I/O 设备。
使用 _open_osfhandle
将新句柄与相应的 C std 文件描述符相关联(返回文件描述符编号)。
使用上述dup2
技术重定向返回的文件描述符。
这是一个用于重定向stdout
的示例sn-p:
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);
附:如果您可以在程序内部不进行 I/O 重定向,那么您可以简单地在命令行中使用控制台的 I/O 重定向,使用一个小批处理文件:
@echo off
start "my_gui_app" "path/to/my_gui_app.exe" 1> "path/to/log.txt"
【讨论】:
这里非常重要的一点是dup2
必须使用直接数字而不是_fileno(stdout)
,因为文档说它可以返回负数,如果标准句柄已经关闭(父进程有称为 CreateProcess 无标准句柄继承)。否则所有顺序调用都将失败,这意味着_close(fd)
将关闭句柄而不重复!以上是关于在win32中重定向标准输出不会重定向标准输出的主要内容,如果未能解决你的问题,请参考以下文章