2

I want to be able to redirect all standard output of a Windows application to the Win32 standard output handle, instead of using a console handle.

For context, the Emacs Win32 FAQ says this:

Programs that explicitly use a handle to the console (CON or CON:) instead of stdin and stdout cannot be used as subprocesses to Emacs, and they will also not work in shell-mode [...] There is no convenient way for either Emacs or any shell used in shell-mode to redirect the input and output of such processes from the console to input and output pipes. The only workaround is to use a different implementation of the program that does not use the console directly. (https://www.gnu.org/software/emacs/manual/html_node/efaq-w32/Subprocess-hang.html)

I'm experiencing the issue the FAQ describes: when started from Emacs, the program will run but none of its output is displayed in Emacs' shell window until the program exits, as if stdout was buffered and didn't flush while it was running. The same program will output stdout in real-time as expected if run from cmd.exe or ConEmu.

What I want to accomplish is making all the output of the program appear in an Emacs shell so I can jump to a compiler error location as soon as it's printed. I'm willing to rewrite the source of the program to accomplish this. I want to know what "different implementation" details are necessary to accomplish this by using "stdin and stdout" instead of "a handle to the console".

From what I understand:

  1. There are multiple kinds of I/O in Windows, including the C runtime (printf, etc.) and the Win32 layer (ReadFile, etc.).
  2. I'm assuming "stdin and stdout" refer to STD_INPUT_HANDLE and STD_OUTPUT_HANDLE which can be modified by SetStdHandle().
  3. External DLLs loaded by a program will inherit the C runtime stdin and stdout pipes, but this happens on program initialization, before any calls to SetStdHandle() are called, so there is something else you have to do to redirect their output.

I've tried using this code:

#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;
}

This has the desired effect, but only on usages of printf and std::cout within the same binary as the redirection code. My program loads an external DLL that uses printf and none of its output is redirected.

Also, I don't want to use AllocConsole() or similar since that opens a separate window, which does not integrate with the parent process (Emacs). What I want is to redirect all stdout so it shows up within the same terminal/console that is running the program instead of a different window (if that makes sense). Since I don't use AllocConsole(), then code like _open_osfhandle((long)hStdout, O_WRONLY|O_TEXT) always returns -1 for some reason, so it seems like I can't use dup2() as described here.

See also:

nonbirithm
  • 133
  • 1
  • 11
  • 1
    Not "as if stdout was buffered", it is buffered when isatty() returns 0. Write to stderr instead or setvbuf() or explicitly fflush(). – Hans Passant Nov 11 '19 at 20:51

1 Answers1

2

I used the code example I provided above, then called setvbuf(stdout, NULL, _IONBF, 4096) to set non-buffered output and got the desired result.

nonbirithm
  • 133
  • 1
  • 11