C++ 更换系统(“颜色”)

C++ 更换系统(“颜色”),c++,C++,我需要一个像系统颜色一样的方法,而不使用系统调用。我知道有setConsoleTextAttribute(),但这并不能用只有颜色的新字符填充整个前景和背景。我使用的是windows7,尽管根据您的评论,我想使其与所有windows兼容,但这里有一个更完整的解决方案,可以解决您试图解决的问题。它基于我的原始答案(可以在这个答案的末尾找到) 我发现了WindowsAPI中的一个限制,即它无法读取80列乘以300行的默认控制台模式窗口的整个屏幕缓冲区。由于Windows进程堆不足,它会导致一个错误\

我需要一个像系统颜色一样的方法,而不使用系统调用。我知道有setConsoleTextAttribute(),但这并不能用只有颜色的新字符填充整个前景和背景。我使用的是windows7,尽管根据您的评论,我想使其与所有windows兼容,但这里有一个更完整的解决方案,可以解决您试图解决的问题。它基于我的原始答案(可以在这个答案的末尾找到)

我发现了WindowsAPI中的一个限制,即它无法读取80列乘以300行的默认控制台模式窗口的整个屏幕缓冲区。由于Windows进程堆不足,它会导致一个错误\u内存不足\u错误(从一些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;
}

是的,如果你想更改属性,你必须“重画”角色。这不起作用,因为系统没有转换所有文本,也没有转换所有未来文本的正确颜色。这并不意味着替换。这是为了说明你是如何完成任务的。添加一行代码和修改两行代码可能会为您的问题提供准确的解决方案,但我并没有试图为您编写代码。哦,我会尝试调整它。问题是从系统输出文本(暂停),如果您要添加
SetConsoleTextAttribute()
对于
ConsoleFillDisplayWithAttribute()
函数,未来的文本将以指定的颜色显示。与其将读写限制在窗口的大小(
&info.srWindow
),不如将其增加到控制台的大小,这样可能更符合您的要求。如果今晚我有时间,我会调整它,使其更符合您的需求,尽管我的初衷只是给您一些如何继续使用现有函数的示例。
#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;
}