1

I would like to capture printf output in a Win32 application (no console) without creating a temporary file or using ConsoleAlloc(). Is this possible? I've got a library that emits read/write status messages to stdout and displays them in a console. I want to create a Win32 app that uses this library and parses the data in real-time to update the app. I can't change the code in the library.

I can get it working with a temporary file, but this is slow and there is lots of disk IO activity:

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    freopen_s((FILE**)stdout, "tempfile.out", "w", stdout);
    printf("Redirected this to a file.");
    fclose(stdout);
}

And I can get it working with fprintf or fwrite as below, but regular print doesn't work. I think this is because the destination file descriptor is invalid in _dup2. It looks like printf is running as it returns the number of bytes in the string but it is pointing to stdout which is an invalid fd. If I step through the code DstHandle is -2, which is invalid. It still doesn't work if I replace the value with 1 (what _fileno(stdout) is supposed to be).

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow)
{
    HANDLE hRead, hWrite;
    CreatePipe(&hRead, &hWrite, NULL, 0);
    //SetStdHandle(STD_OUTPUT_HANDLE, hWrite);

    int fd = _open_osfhandle((intptr_t)hWrite, O_WRONLY);
    FILE* f = _fdopen(fd, "w"); // Associate a stream f with a fd

    const char* wbuffer = " --- Hello World! --- \n";
    fwrite(wbuffer, 1, strlen(wbuffer), f);

    printf("Testing printf\n");
    fprintf(f, "Testing fprintf"); // This works

    //*stdout = *fd;
    int DstHandle = (_fileno)(stdout); // Get the stdout (destination) fd. Result undefined if stream doesn't specify an open file.
    int res = _dup2(fd, DstHandle);
    setvbuf(stdout, NULL, _IONBF, 0); // Unbuffered, no need for fflush()
    //freopen_s(&f, "CONOUT$", "w", stdout); // Crash... there is no console here.
    
    printf("Testing printf again\n");

    //fflush(f);
    fclose(f); // Also calls _close()

    DWORD bytesRead = 0;
    char buffer[BUFFSIZE + 1] = { 0 };
    ReadFile(hRead, buffer, BUFFSIZE, &bytesRead, NULL);
    
    return 0;
}

My problem seems to be around the file stream for stdout not being able to reassigned a new value or refreshed. For example, this works and requires freopen to reassign the stream, but has a console:

AllocConsole();
freopen_s((FILE**)stdout, "CONOUT$", "w", stdout);
//HANDLE hConOut = CreateFile(L"CONOUT$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
//SetStdHandle(STD_OUTPUT_HANDLE, hConOut); 
printf("This works\n");

For reference, there is a similar question here:

Mr. Anderson
  • 83
  • 1
  • 8
  • Not sure if this is *directly* possible the way you want in a single process, but *indirectly* it is doable if you have your main process spawn a separate process with [redirected output to a pipe](https://learn.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output) to handle the work of the library, and then the main process can capture the output of that pipe. – Remy Lebeau Mar 25 '22 at 05:25
  • I sugget you could refer to the threads: https://stackoverflow.com/questions/5911147/how-to-redirect-printf-output-back-into-code https://stackoverflow.com/questions/21646641/where-does-printf-write-in-a-windows-non-console-application – Jeaninez - MSFT Mar 25 '22 at 07:47
  • Something I didn't notice before on this page https://learn.microsoft.com/en-us/cpp/c-runtime-library/stdin-stdout-stderr?view=msvc-170 it states the file stream pointers are constants and to change them you must use freopen. So are there any special values that can be passed to this function for the filename other than "CONOUT$"? – Mr. Anderson Mar 25 '22 at 09:20
  • @Mr.Anderson As far as I'm concerned, you could try to create a pipe, make the writable end of the pipe the new stdout, and then you could read from the readable part of the pipe. – Jeaninez - MSFT Mar 29 '22 at 06:18

0 Answers0