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:
- All the answers use ConsoleAlloc(): How to view printf output in a Win32 application on Visual Studio 2010?
- Get a C Runtime fd from a handle: Is there a Windows equivalent to fdopen for HANDLEs?