You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用WriteConsoleOutputAttribute时控制台异常颜色的原因排查

控制台WriteConsoleOutputAttribute批量着色导致颜色混乱问题解决

我尝试给控制台窗口特定位置的字符添加颜色编码优化显示,此前操作都正常,但在对第4行文本执行简单着色时,出现大量前景色与背景色混乱的异常情况。

异常效果截图:
WriteConsoleOutputAttribute 异常效果

可复现问题的代码:

#define UNICODE

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

#pragma comment(lib, "User32.lib")

using namespace std;

int main()
{
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
    HANDLE hStdErr = GetStdHandle(STD_ERROR_HANDLE);
    
    SMALL_RECT consoleCoords{0, 0, 79, 19};
    COORD sbSize{80, 20};
    
    if(!SetConsoleWindowInfo(hStdOut, TRUE, &consoleCoords)) /* HANDLE hConsoleOutput, BOOL bAbsolute, const SMALL_RECT* lpConsoleWindow */
    {
        wcout << L"SetConsoleWindowInfo failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    if(!SetConsoleScreenBufferSize(hStdOut, sbSize)) /* HANDLE hConsoleOutput, COORD dwSize */
    {
        wcout << L"SetConsoleScreenBufferSize failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    DWORD cCharsWritten{};
    DWORD attrsWritten{};
    CONSOLE_SCREEN_BUFFER_INFO bufferInfo{};
    DWORD dwConsoleSz{};
    system("pause");
    
    if(!GetConsoleScreenBufferInfo(hStdOut, &bufferInfo))
    {
        wcout << L"GetConsoleScreenBufferInfo failed. Error Code: " << GetLastError() << '\n';
        system("pause");
        return 0;
    }
    dwConsoleSz = bufferInfo.dwSize.X * bufferInfo.dwSize.Y; /* X (character cells) * Y (character cells) */
    COORD cursorHome{0, 0};
    /* Now we fill the entire screen with blanks. */
    if(!FillConsoleOutputCharacter(
            hStdOut,
            L' ',
            dwConsoleSz,
            cursorHome,
            &cCharsWritten
        )
    )
    {
        wcout << L"FillConsoleOutputCharacter failed. Error Code: " << GetLastError() << '\n';
        system("pause");
        return 0;
    }
    if(!SetConsoleCursorPosition(hStdOut, cursorHome))
        /*Research is needed regarding this function as it does not seem to move the cursor. */
    {
        wcout << L"SetConsoleCursorPosition failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    
    wchar_t topmiddlebottomRow[] = L"================";
    wchar_t separatorString[] = L"================================================================================";
    
    LPCWSTR writeRow = topmiddlebottomRow;
    LPCWSTR writeSeparator = separatorString;
    
    WORD S_attr = FOREGROUND_GREEN;
    
    if(!WriteConsoleOutputCharacterW(hStdOut, writeRow, 16, COORD{64, 0}, &cCharsWritten))
    {
        wcout << L"WriteConsoleOutputCharacterW failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    if(!WriteConsoleOutputCharacterW(hStdOut, writeRow, 16, COORD{64, 1}, &cCharsWritten))
    {
        wcout << L"WriteConsoleOutputCharacterW failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    if(!WriteConsoleOutputCharacterW(hStdOut, writeRow, 16, COORD{64, 2}, &cCharsWritten))
    {
        wcout << L"WriteConsoleOutputCharacterW failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    if(!WriteConsoleOutputCharacterW(hStdOut, writeSeparator, 80, COORD{0, 4}, &cCharsWritten))
    {
        wcout << L"WriteConsoleOutputCharacterW failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    if(!WriteConsoleOutputAttribute(hStdOut, &S_attr, 80, COORD{0, 4}, &attrsWritten))
    {
        wcout << L"WriteConsoleOutputAttribute failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
    
    
    while(1)
    {
    }
    
    return 0;
}

问题原因

调用WriteConsoleOutputAttribute时,第二个参数要求传入一个属性数组的起始地址,函数会从该地址开始连续读取nLength个(这里是80个)WORD类型的属性值。但原代码只定义了单个S_attr变量,函数读取时会越界访问后续未初始化的内存,这些垃圾数据被当作属性值应用到字符上,导致颜色混乱。

解决方法

方法1:创建对应长度的属性数组

提前初始化一个包含80个相同属性值的数组,确保函数读取的所有属性都是合法且正确的:

// 替换原单个S_attr定义
WORD S_attrs[80];
// 初始化所有元素为绿色前景色
for (int i = 0; i < 80; ++i) {
    S_attrs[i] = FOREGROUND_GREEN;
}

// 调用WriteConsoleOutputAttribute时传入数组地址
if(!WriteConsoleOutputAttribute(hStdOut, S_attrs, 80, COORD{0, 4}, &attrsWritten))
{
    wcout << L"WriteConsoleOutputAttribute failed. Error Code: " << GetLastError() << L'\n';
    system("pause");
    return 0;
}

方法2:循环逐个设置属性

如你自己发现的,通过循环逐个给每个字符设置属性,避免内存越界问题:

COORD pos;
pos.Y = 4;
for (int i = 0; i < 80; ++i) {
    pos.X = i;
    if(!WriteConsoleOutputAttribute(hStdOut, &S_attr, 1, pos, &attrsWritten))
    {
        wcout << L"WriteConsoleOutputAttribute failed. Error Code: " << GetLastError() << L'\n';
        system("pause");
        return 0;
    }
}

额外说明

  • #pragma comment(lib, "User32.lib")确实多余,控制台相关API依赖的是Kernel32.lib,编译器通常会自动链接该库。

内容的提问来源于stack exchange,提问作者nore

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 08:59:17