我需要一种不使用系统调用就像系统颜色一样的方法。我知道有setConsoleTextAttribute(),但它不会填充完整的前景和背景,只有颜色的新字符。我正在使用windows7,虽然我想使这与所有Windows兼容
答案 0 :(得分:0)
根据您的意见,这是您要解决的问题的更完整的解决方案。它基于我的原始答案(可以在本答复的末尾找到)。
我在Windows API中发现了一个限制,即无法读取整个屏幕缓冲区的80列乘300行的默认控制台模式窗口。由于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;
}
原始答案如下:
如果我理解您的要求,您希望能够更改整个控制台模式窗口的属性。您可能需要查看ReadConsoleOutput
和WriteConsoleOutput
函数。您可以使用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;
}