tags:

views:

2928

answers:

7

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 that just forcing std::cout to be a glorified fputc?

A: 

The ios library has a function that lets you re-sync C++ IO to whatever the standard C IO is using: ios::sync_with_stdio().

There's a nice explanation here: http://dslweb.nwnexus.com/~ast/dload/guicon.htm.

DarthPingu
Unfortunately, I tried that (first line in my WinMain) and it didn't seem to make a difference. Only C style IO got sent to the console.
Evan Teran
Do you mean you had ios::sync_with_stdio() as the first line in WinMain? I would expect you to need to call it after setting up the C IO instead.
DarthPingu
tried that too. Unfortunately, no dice.
Evan Teran
A: 

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
I'll look into it. Just a note, I'm using 2008, not 2005.
Evan Teran
For the Windows API or standard library, 2005 isn't much different than 2008. There is a difference between VC6 and 2003, however (but that's probably not enough to make that an issue.)
Raymond Martineau
Unfortunately, they changed something between 2003 and 2008 making std::cout no longer redirect given the code I used :(.
Evan Teran
A: 

This link might provide some information: Adding Console I/O to a Win32 GUI App.

Mark Ransom
Unfortunately that's the same link as DarthPingu supplied. Also basically it boils down doing stuff like "*stderr = *fp" which I think is an undefined behavior way of doing freopen. Also I've tried the sync_with_stdio that the page recommended. No dice in 2008 (used to work in 2003).
Evan Teran
A: 

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

Perpetualcoder
Not using C# or managed C++. So I have no "System::Diagnostics::Process::Execute"
Evan Teran
A: 

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.

Will Dean
+1  A: 

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.

Dmitry Khalatov
+1  A: 

I'm posting a standard 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 redirect. Thanks to everyone for your unput.

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