从重定向的STDOUT(C,Win32 API,Qt)读取Unicode

从重定向的STDOUT(C,Win32 API,Qt)读取Unicode,第1张

概述我有一个动态加载插件DLL的C应用程序. DLL通过std :: cout和std :: wcout发送文本输出.基于Qt的UI必须从DLL中获取所有文本输出并显示它. 由于运行时库差异,DLL可能具有不同的cout / wcout实例,因此使用流缓冲区替换的方法并不完全有效.因此,我已应用 Windows特定的STDOUT重定向,如下所示: StreamReader::StreamReader( 我有一个动态加载插件DLL的C应用程序. DLL通过std :: cout和std :: wcout发送文本输出.基于Qt的UI必须从DLL中获取所有文本输出并显示它.
由于运行时库差异,DLL可能具有不同的cout / wcout实例,因此使用流缓冲区替换的方法并不完全有效.因此,我已应用 Windows特定的STDOUT重定向,如下所示:

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,_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,BUFFER_SIZE);                    if (Readfile(stdoutRead,NulL)                        && nofRead)                    {                        s.append(buf);                    }                }            }            else            {                memset(buf,BUFFER_SIZE);                if (Readfile(stdoutRead,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,BUFFER_SIZE);        }    }    // clean up on thread finish    cleanUp();}

但是,这个解决方案似乎有一个障碍 – C运行时库,它依赖于语言环境.因此,发送到wcout的任何输出都没有到达我的缓冲区,因为C运行时会截断UTF-16编码字符串中存在的非可打印ASCII字符的字符串.调用setlocale()演示,C运行时执行字符串重新/编码. setlocale()对我来说没有任何帮助,因为不了解文本的语言或语言环境,因为插件DLL从系统外部读取并且可能混合了不同的语言.
在给出一个N-thought之后我决定放弃这个解决方案并恢复到cout / wcout缓冲区替换并将DLL的要求调用初始化方法由于两个原因:UTF16没有传递给我的缓冲区,然后是计算编码的问题在缓冲区.但是,我仍然很想知道是否有一种方法可以将UTF-16字符串通过C运行时“按原样”导入管道,而不需要依赖于语言环境的转换?

附:任何关于cout / wcout重定向到UI而不是两个提到的方法的建议也是受欢迎的:)

先感谢您!

解决方法 这里的问题是从wchar_t到char的代码转换完全是在插件DLL中完成的,无论它碰巧使用什么cout / wcout实现(正如你所说的那样,可能与主要的实现不一样)应用程序正在使用).因此,使其表现不同的唯一方法是以某种方式拦截该机制,例如使用streambuf替换.

但是,正如您所暗示的那样,您在主应用程序中编写的任何代码都不一定与DLL使用的库实现兼容.例如,如果在主应用程序中实现流缓冲区,则它不一定会使用与DLL中的流缓冲区相同的ABI.所以这很危险.

我建议你实现一个使用与插件相同的C库版本的包装器DLL,因此它保证兼容,并且在这个包装器DLL中对cout / wcout进行必要的干预.它可以动态加载插件,因此可以使用任何使用该库版本的插件重用.或者,您可以创建一些可重用的源代码,可以为每个插件专门编译,从而生成每个插件的清理版本.

一旦DLL被包装,您可以将流缓冲区替换为cout / wcout,将数据保存到内存中,就像我认为您最初计划的那样,并且根本不必弄乱文件句柄.

PS:如果你确实需要制作一个转换为UTF-8和从UTF-8转换的wstream,那么我建议使用Boost的utf8_codecvt_facet作为一种非常巧妙的方式.它易于使用,文档中包含示例代码.(在这种情况下,您必须专门为插件使用的库版本编译Boost版本,但在一般情况下不会.)

总结

以上是内存溢出为你收集整理的从重定向的STDOUT(C,Win32 API,Qt)读取Unicode全部内容,希望文章能够帮你解决从重定向的STDOUT(C,Win32 API,Qt)读取Unicode所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1225204.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-05
下一篇2022-06-05

发表评论

登录后才能评论

评论列表(0条)

    保存