为了账号安全,请及时绑定邮箱和手机立即绑定

将Cout重定向到Windows中的控制台

将Cout重定向到Windows中的控制台

C++
跃然一笑 2019-11-29 10:32:27
我有一个相对较旧的应用程序。通过一些小的更改,它几乎可以在Visual C ++ 2008中完美构建。我注意到的一件事是,我的“调试控制台”运行不正常。基本上,过去,我已经使用AllocConsole()过一个控制台,供调试输出使用。然后,我将使用freopen重定向stdout到它。这与C和C ++风格的IO完美配合。现在,它似乎仅适用于C风格的IO。将类似的东西重定向cout到分配有控制台的正确方法是什么AllocConsole()?这是曾经工作的代码:if(AllocConsole()) {    freopen("CONOUT$", "wt", stdout);    SetConsoleTitle("Debug Console");    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);}编辑:发生在我身上的一件事是,我可以制作一个自定义streambuf,其溢出方法使用C风格的IO编写,并std::cout用它替换默认的流缓冲区。但这似乎是一个解决方案。有没有合适的方法在2008年做到这一点?还是MS忽略了这一点?EDIT2:好的,所以我已经实现了上面阐述的想法。基本上看起来像这样:class outbuf : public std::streambuf {public:    outbuf() {        setp(0, 0);    }    virtual int_type overflow(int_type c = traits_type::eof()) {        return fputc(c, stdout) == EOF ? traits_type::eof() : c;    }};int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {    // create the console    if(AllocConsole()) {        freopen("CONOUT$", "w", stdout);        SetConsoleTitle("Debug Console");        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);      }    // set std::cout to use my custom streambuf    outbuf ob;    std::streambuf *sb = std::cout.rdbuf(&ob);    // do some work here    // make sure to restore the original so we don't get a crash on close!    std::cout.rdbuf(sb);    return 0;}有没有人比强迫std::cout别人变得更好/更清洁的解决方案fputc?
查看完整描述

3 回答

?
阿波罗的战车

TA贡献1862条经验 获得超6个赞

这是解决此问题的函数的最新版本:


void BindCrtHandlesToStdHandles(bool bindStdIn, bool bindStdOut, bool bindStdErr)

{

    // Re-initialize the C runtime "FILE" handles with clean handles bound to "nul". We do this because it has been

    // observed that the file number of our standard handle file objects can be assigned internally to a value of -2

    // when not bound to a valid target, which represents some kind of unknown internal invalid state. In this state our

    // call to "_dup2" fails, as it specifically tests to ensure that the target file number isn't equal to this value

    // before allowing the operation to continue. We can resolve this issue by first "re-opening" the target files to

    // use the "nul" device, which will place them into a valid state, after which we can redirect them to our target

    // using the "_dup2" function.

    if (bindStdIn)

    {

        FILE* dummyFile;

        freopen_s(&dummyFile, "nul", "r", stdin);

    }

    if (bindStdOut)

    {

        FILE* dummyFile;

        freopen_s(&dummyFile, "nul", "w", stdout);

    }

    if (bindStdErr)

    {

        FILE* dummyFile;

        freopen_s(&dummyFile, "nul", "w", stderr);

    }


    // Redirect unbuffered stdin from the current standard input handle

    if (bindStdIn)

    {

        HANDLE stdHandle = GetStdHandle(STD_INPUT_HANDLE);

        if(stdHandle != INVALID_HANDLE_VALUE)

        {

            int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);

            if(fileDescriptor != -1)

            {

                FILE* file = _fdopen(fileDescriptor, "r");

                if(file != NULL)

                {

                    int dup2Result = _dup2(_fileno(file), _fileno(stdin));

                    if (dup2Result == 0)

                    {

                        setvbuf(stdin, NULL, _IONBF, 0);

                    }

                }

            }

        }

    }


    // Redirect unbuffered stdout to the current standard output handle

    if (bindStdOut)

    {

        HANDLE stdHandle = GetStdHandle(STD_OUTPUT_HANDLE);

        if(stdHandle != INVALID_HANDLE_VALUE)

        {

            int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);

            if(fileDescriptor != -1)

            {

                FILE* file = _fdopen(fileDescriptor, "w");

                if(file != NULL)

                {

                    int dup2Result = _dup2(_fileno(file), _fileno(stdout));

                    if (dup2Result == 0)

                    {

                        setvbuf(stdout, NULL, _IONBF, 0);

                    }

                }

            }

        }

    }


    // Redirect unbuffered stderr to the current standard error handle

    if (bindStdErr)

    {

        HANDLE stdHandle = GetStdHandle(STD_ERROR_HANDLE);

        if(stdHandle != INVALID_HANDLE_VALUE)

        {

            int fileDescriptor = _open_osfhandle((intptr_t)stdHandle, _O_TEXT);

            if(fileDescriptor != -1)

            {

                FILE* file = _fdopen(fileDescriptor, "w");

                if(file != NULL)

                {

                    int dup2Result = _dup2(_fileno(file), _fileno(stderr));

                    if (dup2Result == 0)

                    {

                        setvbuf(stderr, NULL, _IONBF, 0);

                    }

                }

            }

        }

    }


    // Clear the error state for each of the C++ standard stream objects. We need to do this, as attempts to access the

    // standard streams before they refer to a valid target will cause the iostream objects to enter an error state. In

    // versions of Visual Studio after 2005, this seems to always occur during startup regardless of whether anything

    // has been read from or written to the targets or not.

    if (bindStdIn)

    {

        std::wcin.clear();

        std::cin.clear();

    }

    if (bindStdOut)

    {

        std::wcout.clear();

        std::cout.clear();

    }

    if (bindStdErr)

    {

        std::wcerr.clear();

        std::cerr.clear();

    }

}

为了定义此功能,您需要以下一组包括:


#include <windows.h>

#include <io.h>

#include <fcntl.h>

#include <iostream>

简而言之,该函数将C / C ++运行时标准输入/输出/错误句柄与与Win32进程关联的当前标准句柄同步。如文档中所述,AllocConsole为我们更改了这些过程句柄,因此所需要做的就是在AllocConsole之后调用该函数以更新运行时句柄,否则,我们将剩下初始化运行时时锁住的句柄。基本用法如下:


// Allocate a console window for this process

AllocConsole();


// Update the C/C++ runtime standard input, output, and error targets to use the console window

BindCrtHandlesToStdHandles(true, true, true);

此功能经过了多次修订,因此,如果您对历史信息或替代方法感兴趣,请检查对此答案的编辑。当前的答案是解决此问题的最佳方法,它具有最大的灵活性,并且可以在任何Visual Studio版本上使用。


查看完整回答
反对 回复 2019-11-29
?
Smart猫小萌

TA贡献1911条经验 获得超7个赞

我以答案形式发布了一个便携式解决方案,因此可以接受。基本上我取代cout的streambuf与一个被用c文件实现的I / O,其不结束被重新定向。感谢大家的投入。


class outbuf : public std::streambuf {

public:

    outbuf() {

        setp(0, 0);

    }


    virtual int_type overflow(int_type c = traits_type::eof()) {

        return fputc(c, stdout) == EOF ? traits_type::eof() : c;

    }

};


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) {

    // create the console

    if(AllocConsole()) {

        freopen("CONOUT$", "w", stdout);

        SetConsoleTitle("Debug Console");

        SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);  

    }


    // set std::cout to use my custom streambuf

    outbuf ob;

    std::streambuf *sb = std::cout.rdbuf(&ob);


    // do some work here


    // make sure to restore the original so we don't get a crash on close!

    std::cout.rdbuf(sb);

    return 0;

}

查看完整回答
反对 回复 2019-11-29
  • 3 回答
  • 0 关注
  • 787 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信