首页 > 解决方案 > WinAPI - 如何将程序的所有标准输出和外部 DLL 重定向到 Win32 标准输出句柄?

问题描述

我希望能够将 Windows 应用程序的所有标准输出重定向到 Win32 标准输出句柄,而不是使用控制台句柄。

对于上下文,Emacs Win32 FAQ 是这样说的:

显式使用控制台句柄(CON 或 CON:)而不是 stdin 和 stdout 的程序不能用作 Emacs 的子进程,它们也不能在 shell 模式下工作 [...] Emacs 或在 shell 模式下使用的任何 shell 将此类进程的输入和输出从控制台重定向到输入和输出管道。唯一的解决方法是使用不直接使用控制台的程序的不同实现。(https://www.gnu.org/software/emacs/manual/html_node/efaq-w32/Subprocess-hang.html

我遇到了常见问题解答描述的问题:从 Emacs 启动时,程序将运行,但shell在程序退出之前,它的任何输出都不会显示在 Emacs 的窗口中,就好像 stdout 被缓冲并且在运行时没有刷新一样。cmd.exe如果从或 ConEmu运行,相同的程序将按预期实时输出标准输出。

我想要完成的是让程序的所有输出都出现在 Emacs shell 中,这样我就可以在打印后立即跳转到编译器错误位置。我愿意重写程序的源代码来实现这一点。我想知道通过使用“stdin and stdout”而不是“控制台句柄”来完成此操作需要哪些“不同的实现”细节。

据我了解:

  1. Windows 中有多种 I/O,包括 C 运行时(printf等)和 Win32 层(ReadFile等)。
  2. 我假设“stdin 和 stdout”指代STD_INPUT_HANDLE并且STD_OUTPUT_HANDLE可以通过SetStdHandle().
  3. 程序加载的外部 DLL 将继承 C 运行时stdinstdout管道,但这发生在程序初始化时,在调用任何调用之前SetStdHandle(),因此您必须做一些其他事情来重定向它们的输出。

我试过使用这段代码:

#include <windows.h>
#include <cstdlib>

int main()
{
    HANDLE hStdout = CreateFile(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    HANDLE hStdin = CreateFile(L"CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
        NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

    SetStdHandle(STD_OUTPUT_HANDLE, hStdout);
    SetStdHandle(STD_ERROR_HANDLE, hStdout);
    SetStdHandle(STD_INPUT_HANDLE, hStdin);

    std::cout << "Hello, world." << std::endl;
    printf("Hello, world.\n");
    std::cin.get();

    return 0;
}

这具有预期的效果,但仅限于与重定向代码相同的二进制文件的用法printf和内部。std::cout我的程序加载了一个使用的外部 DLL,printf并且它的任何输出都没有被​​重定向。

另外,我不想使用AllocConsole()或类似,因为它会打开一个单独的窗口,该窗口不与父进程(Emacs)集成。我想要的是重定向所有标准输出,以便它显示在运行程序的同一个终端/控制台中,而不是另一个窗口(如果有意义的话)。由于我不使用AllocConsole(),所以代码_open_osfhandle((long)hStdout, O_WRONLY|O_TEXT)总是-1由于某种原因返回,所以看起来我不能像这里dup2()描述的那样使用。

也可以看看:

标签: c++cwindowsprintf

解决方案


我使用了上面提供的代码示例,然后调用setvbuf(stdout, NULL, _IONBF, 4096)以设置非缓冲输出并获得所需的结果。


推荐阅读