У меня есть приложение C ++, которое динамически загружает подключаемые библиотеки DLL.DLL отправляет текстовый вывод через std :: cout и std :: wcout.Интерфейс на основе Qt должен захватывать весь текстовый вывод из DLL и отображать его.Подход с заменой потокового буфера не полностью работает, так как DLL могут иметь разные экземпляры cout / wcout из-за различий в библиотеках времени выполнения.Таким образом, я применил перенаправление STDOUT для Windows следующим образом:
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();
}
Однако у этого решения есть препятствие - библиотека времени выполнения C, которая зависит от локали.Таким образом, любой вывод, отправленный в wcout, не достигает моего буфера, потому что среда выполнения C обрезает строки до непечатных символов ASCII, присутствующих в строках в кодировке UTF-16.Вызов setlocale () демонстрирует, что среда выполнения C выполняет пере / кодирование строк. setlocale () мне не поможет, по той причине, что нет знания языка или локали текста, так как подключаемые библиотеки DLL читаются извне системы и могут смешиваться разные языки.После размышлений я решил отказаться от этого решения и вернуться к замене буфера cout / wcout и наложить требование к DLL вызывать метод инициализации по двум причинам: UTF16 не передается в мой буфер, а затем проблема выяснения кодировкив буфере.Тем не менее, мне все еще интересно, есть ли способ получить строки UTF-16 через среду выполнения C в канал «как есть», без зависящего от локали преобразования?
ps любые предложения по перенаправлению cout / wcout в пользовательский интерфейса не два упомянутых подхода приветствуются:)
Заранее спасибо!