在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 流(这些被printfscanf、...使用) POSIX I/O 描述符(这些是readwrite、...使用的整数) 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 运行时库将其 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中重定向标准输出不会重定向标准输出的主要内容,如果未能解决你的问题,请参考以下文章

如何在C#中重定向IronPython的标准输出?

如何在 C# 中重定向 IronPython 的标准输出?

Linux中重定向管道和grep命令总结

Linux中重定向管道和grep命令总结

Linux中重定向管道和grep命令总结

linux中重定向学习总结