2013-02-05 96 views
0

我需要一種方法,可以像系統顏色一樣不使用系統調用。我知道這裏有setConsoleTextAttribute(),但是它並沒有用全新的字符填充整個前景和背景。我使用的windows7雖然我很想做所有的Windows替換爲系統(「顏色」)

+0

是的,如果你想改變屬性你必須「重繪」角色。 –

回答

0

根據您的評論此兼容,這裏是你正在試圖解決的問題更完整的解決方案。它基於我的原始答案(可以在答案的最後找到答案)。

我在Windows API發現了一個限制,即它不能由300行讀取80列的默認控制檯模式窗口,整個屏幕緩衝區。由於Windows進程堆不足,導致ERROR_NOT_ENOUGH_MEMORY錯誤(我從某些Google搜索中可以看出最好)。我最終實現了XxxConsoleOutput函數的包裝,以支持根據需要自動細分屏幕緩衝區,直到函數成功或未能讀取1 X 1(單字符)區域。

同樣,這段代碼很可能不是完美的,它的目的是解釋概念,而不是(必然)爲您提供一個完整的解決方案。但是,它現在填充了整個控制檯(以前我只填充了窗口的可見部分)並設置了控制檯的文本屬性以供將來輸出。

跳過該代碼閱讀原答覆。

#define WIN32_LEAN_AND_MEAN 
#include <Windows.h> 

#include <iostream> 
#include <vector> 

using namespace std; 

// Split a rectangular region into two smaller rectangles 
// based on the largest dimension. 
void SplitRegion(
    SHORT width, SHORT height, 
    COORD dwBufferCoord, const SMALL_RECT& readRegion, 
    COORD& dwBufferCoordA, SMALL_RECT& readRegionA, 
    COORD& dwBufferCoordB, SMALL_RECT& readRegionB) 
{ 
    dwBufferCoordA = dwBufferCoordB = dwBufferCoord; 
    readRegionA = readRegionB = readRegion; 

    if (height >= width) 
    { 
     SHORT half = height/2; 
     dwBufferCoordB.Y += half; 
     readRegionB.Top += half; 
     readRegionA.Bottom = readRegionB.Top - 1; 
    } 
    else 
    { 
     SHORT half = width/2; 
     dwBufferCoordB.X += half; 
     readRegionB.Left += half; 
     readRegionA.Right = readRegionB.Left - 1; 
    } 
} 

// Utility function to figure out the distance 
// between two points. 
template <typename type> 
inline type DiffHelper(type first, type second) 
{ 
    return (second >= first) ? (second - first + 1) : 0; 
} 

// A template that wraps up the shared code common between 
// reading and writing the screen buffer. If it is ever 
// given a region of zero width or height, it will 
// "succeed". If it ever tries to subdivide a 1 by 1 
// region, it will fail. 
template <typename lpBufferType> 
BOOL XferConsoleOutputWrapper(
    HANDLE hConsoleOutput, 
    lpBufferType lpBuffer, 
    COORD dwBufferSize, 
    COORD dwBufferCoord, 
    SMALL_RECT& xferRegion, 
    BOOL (WINAPI * xferConsoleOutput)(
     HANDLE, lpBufferType, COORD, COORD, PSMALL_RECT)) 
{ 
    SHORT width = DiffHelper(xferRegion.Left, xferRegion.Right); 
    SHORT height = DiffHelper(xferRegion.Top, xferRegion.Bottom); 

    if ((width == 0) || (height == 0)) 
    { 
     return TRUE; 
    } 

    BOOL success = xferConsoleOutput(hConsoleOutput, 
     lpBuffer, dwBufferSize, dwBufferCoord, &xferRegion); 
    if (!success) 
    { 
     if ((GetLastError() == ERROR_NOT_ENOUGH_MEMORY) && 
      ((width * height) > 1)) 
     { 
      COORD dwBufferCoordA, dwBufferCoordB; 
      SMALL_RECT xferRegionA, xferRegionB; 
      SplitRegion(
       width, height, 
       dwBufferCoord, xferRegion, 
       dwBufferCoordA, xferRegionA, 
       dwBufferCoordB, xferRegionB); 
      success = 
       XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize, 
        dwBufferCoordA, xferRegionA, xferConsoleOutput) && 
       XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize, 
        dwBufferCoordB, xferRegionB, xferConsoleOutput); 
     } 
    } 
    return success; 
} 

// ReadConsoleOutput failed to read an 80 by 300 character screen 
// buffer in a single call, resulting in ERROR_NOT_ENOUGH_MEMORY. 
// ReadConsoleOutputWrapper will subdivide the operation into 
// smaller and smaller chunks as needed until it succeeds in reading 
// the entire screen buffer. 
inline BOOL ReadConsoleOutputWrapper(
    HANDLE hConsoleOutput, 
    PCHAR_INFO lpBuffer, 
    COORD dwBufferSize, 
    COORD dwBufferCoord, 
    SMALL_RECT& readRegion) 
{ 
    return XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize, 
     dwBufferCoord, readRegion, ReadConsoleOutput); 
} 

// WriteConsoleOutputWrapper will subdivide the operation into 
// smaller and smaller chunks as needed until it succeeds in writing 
// the entire screen buffer. This may not be necessary as 
// WriteConsoleOutput never failed, but it was simple to implement 
// so it was done just to be safe. 
inline BOOL WriteConsoleOutputWrapper(
    HANDLE hConsoleOutput, 
    const CHAR_INFO* lpBuffer, 
    COORD dwBufferSize, 
    COORD dwBufferCoord, 
    SMALL_RECT& writeRegion) 
{ 
    return XferConsoleOutputWrapper(hConsoleOutput, lpBuffer, dwBufferSize, 
     dwBufferCoord, writeRegion, WriteConsoleOutput); 
} 

void ConsoleFillWithAttribute(WORD fillAttribute) 
{ 
    // Get the handle to the output 
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 

    // Get the information for the current screen buffer 
    CONSOLE_SCREEN_BUFFER_INFO info; 
    GetConsoleScreenBufferInfo(hStdout, &info); 

    // Allocate a vector to hold the visible screen buffer data 
    vector<CHAR_INFO> buffer(info.dwSize.X * info.dwSize.Y); 

    // Initialize a couple of pointers to the begin and end of the buffer 
    CHAR_INFO* begin = buffer.data(); 
    CHAR_INFO* end = begin + buffer.size(); 

    // Start at the upper left corner of the screen buffer. 
    COORD coord; 
    coord.X = coord.Y = 0; 

    // Initialize the region to encompass the entire screen buffer. 
    SMALL_RECT region; 
    region.Left = region.Top = 0; 
    region.Right = info.dwSize.X - 1; 
    region.Bottom = info.dwSize.Y - 1; 

    // Read the buffer from the console into the CHAR_INFO vector. 
    ReadConsoleOutputWrapper(hStdout, buffer.data(), info.dwSize, coord, region); 

    // Change all the attributes to the specified fill attribute. 
    while (begin != end) 
    { 
     begin->Attributes = fillAttribute; 
     ++begin; 
    } 

    // Write the buffer from the CHAR_INFO vector back to the console. 
    WriteConsoleOutputWrapper(hStdout, buffer.data(), info.dwSize, coord, region); 

    // Finally, set the console text attribute to the fill attribute 
    // so that all new text will be printed in the same manner as 
    // the attributes we just changed. 
    SetConsoleTextAttribute(hStdout, fillAttribute); 
} 

int main() 
{ 
    cout << "I would like to fill up the console with some text." << endl; 
    cout << "The quick brown fox jumped over the lazy dogs." << endl; 

    for (int i = 0; i < 100; ++i) 
    { 
     cout << ' ' << i; 
    } 

    cout << endl; 

    ConsoleFillWithAttribute(
     BACKGROUND_BLUE | FOREGROUND_INTENSITY | 
      FOREGROUND_RED | FOREGROUND_GREEN); 

    cout << endl; 

    cout << "This should also be printed in the new attribute" << endl; 

    return 0; 
} 

原來答案是如下:

如果我明白你問什麼,你希望能夠改變的屬性整個控制檯模式窗口。您可能需要查看ReadConsoleOutputWriteConsoleOutput函數。您可以使用ReadConsoleOutput將部分或全部控制檯緩衝區讀入內存,然後根據需要爲應用程序操作屬性數據,然後使用WriteConsoleOutput將內存寫回控制檯輸出緩衝區。

下面是一些代碼,這將改變屬性爲控制檯的當前顯示部分(假設輸出沒有被重定向到一個非控制檯手柄):

#define WIN32_LEAN_AND_MEAN 
#include <Windows.h> 

#include <iostream> 
#include <vector> 

using namespace std; 

void ConsoleFillDisplayWithAttribute(WORD fillAttribute) 
{ 
    // Get the handle to the output 
    HANDLE hStdout = GetStdHandle(STD_OUTPUT_HANDLE); 

    // Get the information for the current screen buffer 
    CONSOLE_SCREEN_BUFFER_INFO info; 
    GetConsoleScreenBufferInfo(hStdout, &info); 

    // Calculate the size of the displayed portion of the screen buffer 
    COORD size; 
    size.X = (info.srWindow.Right - info.srWindow.Left + 1); 
    size.Y = (info.srWindow.Bottom - info.srWindow.Top + 1); 

    // Allocate a vector to hold the visible screen buffer data 
    vector<CHAR_INFO> buffer(size.X * size.Y); 

    COORD coord; 
    coord.X = coord.Y = 0; 

    // Read the buffer from the console into the CHAR_INFO vector 
    ReadConsoleOutput(hStdout, buffer.data(), size, coord, &info.srWindow); 

    // Initialize a couple of pointers to the begin and end of the buffer 
    CHAR_INFO* begin = buffer.data(); 
    CHAR_INFO* end = begin + buffer.size(); 

    // Change all the attributes to the specified fill attribute 
    while (begin != end) 
    { 
     begin->Attributes = fillAttribute; 
     ++begin; 
    } 

    // Write the buffer from the CHAR_INFO vector back to the console 
    WriteConsoleOutput(hStdout, buffer.data(), size, coord, &info.srWindow); 
} 

int main() 
{ 
    cout << "I would like to fill up the console with some text." << endl; 
    cout << "The quick brown fox jumped over the lazy dogs." << endl; 

    for (int i = 0; i < 100; ++i) 
    { 
     cout << ' ' << i; 
    } 

    cout << endl; 

    ConsoleFillDisplayWithAttribute(
     BACKGROUND_BLUE | FOREGROUND_INTENSITY | 
      FOREGROUND_RED | FOREGROUND_GREEN); 

    return 0; 
} 
+0

does not work以及系統不是所有的文字都轉換,並不是所有的未來的文字都是在正確的顏色 –

+0

嗯,這並不意味着作爲替代品的下降。這是爲了說明你如何完成你的任務。添加一行代碼和修改兩行代碼可能會爲您提供確切的解決方案,但我並未嘗試爲您編寫代碼。 – CasaDeRobison

+0

哦生病嘗試來調整其輸出從系統(暫停)的文本和其他來源的犯規出現右 –