Redirecting cout to a console in windows

Viewed 40586

I have an application which is a relatively old. Through some minor changes, it builds nearly perfectly with Visual C++ 2008. One thing that I've noticed is that my "debug console" isn't quite working right. Basically in the past, I've use AllocConsole() to create a console for my debug output to go to. Then I would use freopen to redirect stdout to it. This worked perfectly with both C and C++ style IO.

Now, it seems that it will only work with C style IO. What is the proper way to redirect things like cout to a console allocated with AllocConsole()?

Here's the code which used to work:

if(AllocConsole()) {
    freopen("CONOUT$", "wt", stdout);
    SetConsoleTitle("Debug Console");
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_RED);
}

EDIT: one thing which occurred to me is that I could make a custom streambuf whose overflow method writes using C style IO and replace std::cout's default stream buffer with it. But that seems like a cop-out. Is there a proper way to do this in 2008? Or is this perhaps something that MS overlooked?

EDIT2: OK, so I've made an implementaiton of the idea I spelled out above. Basically it looks like this:

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;
}

Anyone have a better/cleaner solution than just forcing std::cout to be a glorified fputc?

11 Answers

I'm posting a portable solution in answer form so it can be accepted. Basically I replaced cout's streambuf with one that is implemented using c file I/O which does end up being redirected. Thanks to everyone for your input.

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;
}

If console is for debug only, you can just use OutputDebugStringA/OutputDebugStringW functions. Their output directed to Output window in VS if you are in debug mode, otherwise you can use DebugView to see it.

This works with VC++ 2017 for c++ style I/O

AllocConsole();

// use static for scope
static ofstream conout("CONOUT$", ios::out); 
// Set std::cout stream buffer to conout's buffer (aka redirect/fdreopen)
cout.rdbuf(conout.rdbuf());

cout << "Hello World" << endl;

From what I can tell, your code should work with VC 2005, if it's your first activity with the console.

After checking a few possibilities, you might be trying to write something before you allocate the console. Writing to std::cout or std::wcout at that point will fail and you need to clear the error flags before making further output.

Raymond Martineau makes a good point about it being 'the first thing you do'.

I had a redirection problem, which I forget the details of now, where it turned out that very early in the execution of the app, the runtime makes some decisions about output directions which then last for the rest of the application.

After following this through the CRT source, I was able to subvert this mechanism by clearing a variable within the CRT, which made it take another look at things once I'd done my AllocConsole.

Obviously this sort of stuff is not going to be portable, possibly even across toolchain versions, but it might help you out.

After your AllocConsole, step all the way down into the next cout output and find out where it's going and why.

Try this 2 liner:

    AllocConsole(); //debug console
    std::freopen_s((FILE**)stdout, "CONOUT$", "w", stdout); //just works

I don't know, but as to why this is happening, freopen("CONOUT$", "w", stdout); might not redirect the stdout handle in the process parameter block (NtCurrentPeb()->ProcessParameters->StandardOutput) to whatever the LPC call to CSRSS/Conhost returns in response to a request for the stdout handle for the attached console of the process (NtCurrentPeb()->ProcessParameters->ConsoleHandle). It might just make the LPC call and then assign the handle to the FILE * stdout global variable. C++ cout doesn't use FILE * stdout at all, and probably still might not sync with the PEB for the standard handles.

I am not sure I understand the problem completely but if you want to be able to simply spit out data to console for diagnostic purpose.. why dont you try out System::Diagnostics::Process::Execute() method or some method in that namespace??

Apologies in advance if it was irrelevant

Related