У меня есть приложение на C++, которое динамически загружает подключаемые DLL-файлы. DLL отправляет текстовый вывод через std :: cout и std :: wcout. Qt-based UI должен захватить весь текстовый вывод из DLL и отобразить его. Подход с заменой буфера потока не полностью работает, поскольку библиотеки DLL могут иметь разные экземпляры cout/wcout из-за различий в библиотеках времени выполнения. Таким образом, я применил для Windows специфичного Перенаправления STDOUT следующим образом:Чтение Юникода из перенаправленного STDOUT (C++, Win32 API, Qt)
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 runtime усекает строки на непечатаемых ASCII-символах, присутствующих в кодированных строках UTF-16. Вызов setlocale() демонстрирует, что C runtime делает строку re/encoding. setlocale() не помогает мне по той причине, что нет никакого знания языка или языка текста, поскольку подключаемые DLL-файлы читаются извне системы, и могут быть разные языки смешанными. После предоставления N-мысли я решил отказаться от этого решения и вернуться к замене буфера cout/wcout и потребовать, чтобы библиотеки DLL вызывали метод инициализации по двум причинам: UTF16 не переходил в мой буфер, а затем проблема определения кодирование в буфере. Тем не менее, мне все еще интересно, есть ли способ получить строки UTF-16 через C runtime в pipe 'as is', без зависящего от локали преобразования?
p.s. любые предложения по перенаправлению cout/wcout в пользовательский интерфейс, а не по двум упомянутым подходам также приветствуются :)
Заранее благодарю вас!
Вы правы, что проблема заключается в том, (в соответствии с требованиями стандарта) 'std :: wcout'. Это было бы самым простым способом, если бы оба отправителя и получателя использовали обычные функции Windows API. – Philipp
Спасибо, Филипп! Из-за устаревших выпусков плагинов используют cout/wcout, и я должен заставить их работать. –
Если они пишут 'cout', вы можете попытаться заставить их использовать UTF-8, но я не знаю, как обращаться с' wcout', так как для стандартного требуется кодирование некоторой локальной кодировки, которая обычно будет устаревшей 8- бит. – Philipp