2016-08-23 43 views
1

我试图打印到我用winapi创建的新屏幕缓冲区,但是它将转到旧缓冲区并且不显示在屏幕上,是可能,将cout重定向到使用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; 
} 
+2

是的,这是可能的。 –

回答

2

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

基本问题很简单:现有的流缓冲区与指定的文件进行通信,但是(可能)不是与控制台交谈的现有流缓冲区。为了得到这个工作,你需要一个与控制台交谈的人。这是一个相当简单的出发点:

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

[注:这是有些不完整 - 它在控制台输出正常,但如果(例如)复制或分配给它,不好的事情可能发生 - 通常的0/3/5规定适用]

一旦你拥有它的输出写入到控制台,它连接到cout流缓冲是相当琐碎:

console_stream_buffer buff(nBuf); 

std::cout.rdbuf(buff); 

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

下面是使用它快速演示:

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

如果您愿意,您当然可以轻松地编写一个为自己分配控制台屏幕缓冲区的流缓冲区。我现在就把它分开了,但根据你如何使用东西,将它们组合起来可能更容易一些(可能还包括一个叫SetConsoleActiveScreenBufferactivate成员)。尽管如此,这些与你的原始问题没有任何关系,所以我现在就离开它。

+0

我想知道(很确定我已经看到了这个),如果可能在C FILE *级别的简单freopen会做。 –

+0

@ Cheersandhth.-Alf:在这种情况下,我认为(至少本身)不可行。 'freopen'需要一个文件名,在这里我们有一个Windows HANDLE,但没有名字。您可能可以使用'_open_osfhandle'来获取控制台屏幕缓冲区的UNIX样式句柄,然后使用'dup2'将stdout重定向到该屏幕缓冲区。 '_open_osfhandle'仅记录为与文件句柄一起使用,而不是屏幕缓冲区句柄,但是您可以*将控制台屏幕缓冲区句柄传递给'WriteFile',因此它有可能工作。 OTOH,我不确定相比以上节省多少。 –

+0

呃,也许是“CONOUT $”,或者任何标准输出流名称在Windows中。 –

0

@Jerry Coffin谢谢你的回答,但它只打印引号之间的字符,它不适用于变量和数组,std::endl也不起作用。

我认为这个解决方案是处理缓冲区的正确方法,但它有点不完整,就像你说的那样。

与这些工作:cout << a; cout << a[5]; cout << endl;

我写不同的东西,这就像双缓冲。它正在写入标准缓冲区,然后将其显示在第二个缓冲区中。 rwBuffer()通过SMALL_RECT

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

void rwBuffer(HANDLE h1, HANDLE h2) { 
    COORD bufPos = { 0,0 }; // START POINT OF THE R&W BUFFER 
    COORD bufSize = { 5, 2 }; // SIZE OF THE R&W BUFFER 
    CHAR_INFO bufInfo[10]; // (5 * 2) CHAR INFO FOR THE R&W BUFFER 

    SMALL_RECT screenRect = { 0, 0, 4, 1 }; // RESOLUTION FOR READ&WRITE 

    // READ FROM "h1" AND WRITE TO THE "h2" THROUGH "screenRect" 
    ReadConsoleOutput(h1, bufInfo, bufSize, bufPos, &screenRect); 
    WriteConsoleOutput(h2, bufInfo, bufSize, bufPos, &screenRect); 
} 

int main() { 
    HANDLE stdBuffer, secondBuffer; 
    int nArray[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 

    stdBuffer = GetStdHandle(STD_OUTPUT_HANDLE); 
    secondBuffer = CreateConsoleScreenBuffer(
     GENERIC_READ | GENERIC_WRITE, 0, NULL, CONSOLE_TEXTMODE_BUFFER, NULL); 
    SetConsoleActiveScreenBuffer(secondBuffer); 

    for (int i = 0; i < 10; i++) { 
     std::cout << nArray[i]; 
     if (i == 4) std::cout << std::endl; 
    } 

    rwBuffer(stdBuffer, secondBuffer); 
    Sleep(3000); 

    SetConsoleActiveScreenBuffer(stdBuffer); 
    CloseHandle(secondBuffer); 
    return 0; 
} 

发送所述标准缓冲器向第二缓冲器的特定区域也有可能同步与标准光标位置的光标位置。

void syncCursor(HANDLE h2) { 
    CONSOLE_SCREEN_BUFFER_INFO bufInfo; 
    COORD cursorPos; 

    GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &bufInfo); 
    cursorPos.X = bufInfo.dwCursorPosition.X; 
    cursorPos.Y = bufInfo.dwCursorPosition.Y; 

    SetConsoleCursorPosition(h2, cursorPos); 
} 
+0

我确定Jerry Coffin的解决方案适用于数组。简单的原因是他没有改变'std :: cout','std :: ostream'或'operator <<'。 ''架构是模块化的; 'ostream'和'operator <<'将许多不同类型转换为文本。 'streambuf'(Jerry改变了)然后把这个文本移到它应该去的任何地方 - 屏幕,文件,或者现在的备用控制台。你显然在第一部分有问题(转成文本)。尽管如此,std :: endl是一种边界情况:它意味着“将\ n附加到文本并立即将文本发送到流缓冲区”。 – MSalters

+0

@ MSalters:其实他是对的。我编辑了代码,将一个'xsputn'而不是只是'overflow'添加到我的缓冲区类中(这可能是一个相当重要的优化),但是在编辑时,我意外地替换了那里的“溢出”意。我已经添加了“overflow”,这可以纠正错误。 –

+0

@ MSalters谢谢我要去搜索它们,但还有一件事情仍然很奇怪,是不是'SetStdHandle()'想要做这个重定向?为什么它仍然在写入旧的缓冲区? – dismaxedfun

相关问题