将 cout 重定向到使用 winapi 创建的新缓冲区

dis*_*fun 3 c++ console winapi

我正在尝试打印到我用 winapi 创建的新屏幕缓冲区,但它会转到旧缓冲区并且不会显示在屏幕上,是否可以重定向cout到使用 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;
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*fin 5

是的,这是可能的。不,这并不一定是微不足道的。

基本问题相当简单:存在与命名文件对话的现有流缓冲区,但(可能)没有与控制台对话的现有流缓冲区。为了让它发挥作用,您需要一个能够与控制台对话的设备。这是一个相当简单的起点:

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;
    }
};
Run Code Online (Sandbox Code Playgroud)

[注意:这有点不完整 - 它可以很好地执行控制台输出,但是如果(例如)您复制或分配它,则可能会发生不好的事情 - 像大多数流缓冲区一样,您可能不应该能够复制或分配它分配它。]

一旦你有了一个将其输出写入控制台的流缓冲区,将其连接到cout就非常简单了:

console_stream_buffer buff(nBuf);

std::cout.rdbuf(buff);

std:cout << "bleh"; // should go to the console `nBuf`.
Run Code Online (Sandbox Code Playgroud)

这是一个使用它的快速演示:

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);
}
Run Code Online (Sandbox Code Playgroud)

当然,如果您愿意,您可以轻松编写一个为其自身分配控制台屏幕缓冲区的流缓冲区。我暂时将其分开,但根据您使用事物的方式,将它们组合起来可能更有意义(并且可能还包括一个activate调用的成员)。SetConsoleActiveScreenBuffer不过,这些都与您原来的问题无关,所以我现在就离开它。