dismaxedfun dismaxedfun - 3 months ago 27
C++ Question

Redirecting cout to the new buffer that created with winapi

I'm trying to print to the new screen buffer that i've create with winapi but it's going to the old buffer and doesn't show up on the screen, is it possible, redirecting

cout
to the new screen buffer that created by using winapi?

#include <iostream>
#include <Windows.h>

int main() {
HANDLE stdBuf, nBuf;
DWORD numberOfChars;

stdBuf = GetStdHandle(STD_OUTPUT_HANDLE);
nBuf = CreateConsoleScreenBuffer(GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);

SetConsoleActiveScreenBuffer(nBuf);
SetStdHandle(STD_OUTPUT_HANDLE, nBuf);

// THIS SHOWING UP ON THE SCREEN
WriteConsole(nBuf, "SECOND BUFFER", 13, &numberOfChars, NULL);

// THIS IS GOING TO THE FIRST BUFFER
std::cout << "SECOND BUFFER with cout" << std::endl;

Sleep(3000);

SetConsoleActiveScreenBuffer(stdBuf);
CloseHandle(nBuf);

int a = 0;
std::cin >> a;
return 0;
}

Answer

Yes, it's possible. No, it's not necessarily entirely trivial.

The basic problem is fairly simple: there are existing stream buffers to talk to a named file, but (probably) not an existing one to talk to a console. To get this to work, you need one that talks to a console. Here's a fairly simple starting point:

class outbuf : public std::streambuf {
    HANDLE h;
public:
    outbuf(HANDLE h) : h(h) {}
protected:
    virtual int_type overflow(int_type c) override {
        if (c != EOF) {
            DWORD written;
            WriteConsole(h, &c, 1, &written, nullptr);
        }
        return c;
    }

    virtual std::streamsize xsputn(char_type const *s, std::streamsize count) override {
        DWORD written;
        WriteConsole(h, s, count, &written, nullptr);
        return written;
    }
};

[Note: this is somewhat incomplete--it does the Console output fine, but if (for example) you copy or assign it, bad things may happen--the usual rule of 0/3/5 applies.]

Once you have a stream buffer that writes its output to a console, connecting it up to cout is pretty trivial:

console_stream_buffer buff(nBuf);

std::cout.rdbuf(buff);

std:cout << "bleh"; // should go to the console `nBuf`.

Here's a quick demo using it:

int main() {

    HANDLE h = CreateConsoleScreenBuffer(GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
    HANDLE original = GetStdHandle(STD_OUTPUT_HANDLE);

    // Create our stream buffer object
    outbuf ob(h);

    // write to the original buffer
    std::cout << "First console";

    // Set cout to go to the second buffer
    std::cout.rdbuf(&ob);

    // write to it
    std::cout << "Second console";

    // display the second buffer
    SetConsoleActiveScreenBuffer(h);

    // show the second buffer for a few seconds:
    Sleep(5000);

    // restore the original buffer
    SetConsoleActiveScreenBuffer(original);
}

You could, of course, easily write a stream buffer that allocates a console screen buffer for itself, if you wanted to. I left that separate for now, but depending on how you're using things, it may easily make more sense to combine them (and probably include an activate member that calls SetConsoleActiveScreenBuffer as well). None of that is really relevant to your original question though, so I'll leave it for now.