2016-08-23 73 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