I have a C ++ application that dynamically loads plug-in DLL modules. The DLL sends text output through std :: cout and std :: wcout. The Qt-based UI should capture all the text output from the DLL and display it. Replacing the stream buffer does not fully work, since DLLs may have different instances of cout / wcout due to differences in runtime libraries. So I applied the STDOUT redirection for Windows as follows:
StreamReader::StreamReader(QObject *parent) : QThread(parent) { // void } void StreamReader::cleanUp() { // restore stdout SetStdHandle (STD_OUTPUT_HANDLE, oldStdoutHandle); CloseHandle(stdoutRead); CloseHandle(stdoutWrite); CloseHandle (oldStdoutHandle); hConHandle = -1; initDone = false; } bool StreamReader::setUp() { if (initDone) { if (this->isRunning()) return true; else cleanUp(); } do { // save stdout oldStdoutHandle = ::GetStdHandle (STD_OUTPUT_HANDLE); if (INVALID_HANDLE_VALUE == oldStdoutHandle) break; if (0 == ::CreatePipe(&stdoutRead, &stdoutWrite, NULL, 0)) break; // redirect stdout, stdout now writes into the pipe if (0 == ::SetStdHandle(STD_OUTPUT_HANDLE, stdoutWrite)) break; // new stdout handle HANDLE lStdHandle = ::GetStdHandle(STD_OUTPUT_HANDLE); if (INVALID_HANDLE_VALUE == lStdHandle) break; hConHandle = ::_open_osfhandle((intptr_t)lStdHandle, _O_TEXT); FILE *fp = ::_fdopen(hConHandle, "w"); if (!fp) break; // replace stdout with pipe file handle *stdout = *fp; // unbuffered stdout ::setvbuf(stdout, NULL, _IONBF, 0); hConHandle = ::_open_osfhandle((intptr_t)stdoutRead, _O_TEXT); if (-1 == hConHandle) break; return initDone = true; } while(false); cleanUp(); return false; } void StreamReader::run() { if (!initDone) { qCritical("Stream reader is not initialized!"); return; } qDebug() << "Stream reader thread is running..."; QString s; DWORD nofRead = 0; DWORD nofAvail = 0; char buf[BUFFER_SIZE+2] = {0}; for(;;) { PeekNamedPipe(stdoutRead, buf, BUFFER_SIZE, &nofRead, &nofAvail, NULL); if (nofRead) { if (nofAvail >= BUFFER_SIZE) { while (nofRead >= BUFFER_SIZE) { memset(buf, 0, BUFFER_SIZE); if (ReadFile(stdoutRead, buf, BUFFER_SIZE, &nofRead, NULL) && nofRead) { s.append(buf); } } } else { memset(buf, 0, BUFFER_SIZE); if (ReadFile(stdoutRead, buf, BUFFER_SIZE, &nofRead, NULL) && nofRead) { s.append(buf); } } // Since textReady must emit only complete lines, // watch for LFs if (s.endsWith('\n')) // may be emmitted { emit textReady(s.left(s.size()-2)); s.clear(); } else // last line is incomplete, hold emitting { if (-1 != s.lastIndexOf('\n')) { emit textReady(s.left(s.lastIndexOf('\n')-1)); s = s.mid(s.lastIndexOf('\n')+1); } } memset(buf, 0, BUFFER_SIZE); } } // clean up on thread finish cleanUp(); }
However, this solution seems to have a C runtime library - an obstacle that depends on the language. Thus, any output sent to wcout does not reach my buffer because C runtime truncates strings on non-printable ASCII characters present in UTF-16 encoded strings. A call to setlocale () demonstrates that C runtime performs string re-encoding. setlocale () does not help me for the reason that there is no knowledge of the language or the language of the text, since the included DLL files are read from outside the system, and there may be different languages mixed. After providing the N-thought, I decided to abandon this solution and return to replacing the cout / wcout buffer and demand that the DLLs call the initialization method for two reasons: UTF16 did not go into my buffer, and then the problem of determining the encoding in the buffer. However, I'm still wondering if there is a way to get UTF-16 strings via C runtime in pipe 'as is', without a locale-dependent conversion?
ps any suggestions on redirecting cout / wcout to the user interface are welcome, not the two mentioned approaches :)
Thank you in advance!