Win32 Console API在不同终端模拟器中的行为不一致问题求助
代码问题分析与修复
核心问题
你的代码在原生cmd/PowerShell中失效的原因主要有三点:
1. 未检查CreateFile调用结果,可能使用无效句柄
当通过CreateFile("CON")获取控制台句柄时,没有检查是否返回INVALID_HANDLE_VALUE。在原生控制台环境中,该调用可能因权限、设备访问限制等原因失败,导致后续所有依赖该句柄的API(GetConsoleScreenBufferInfo、SetConsoleTextAttribute、WriteConsole)全部失效。
2. 属性获取失败时返回固定值,破坏原有控制台状态
get_text_attributes函数在GetConsoleScreenBufferInfo失败时,直接返回固定值0x70(对应白色背景+黑色前景)。这会导致savedAttr被错误设置为该值,最终重置操作无法恢复控制台原有属性,反而强制将背景改为白色。
3. 不必要使用CreateFile("CON")替代标准句柄
原生控制台中,GetStdHandle(STD_OUTPUT_HANDLE)已经可以正确获取与标准输出关联的控制台句柄,无需通过CreateFile打开CON设备。后者会创建独立的控制台句柄,可能与实际输出流的属性不同步,且增加了权限失败的风险。
修复后的代码
#include <windows.h> #include <io.h> #include <conio.h> #include <fileapi.h> #include <assert.h> #include <stdio.h> typedef struct { HANDLE h; HANDLE hin; WORD savedAttr; } Terminal; #define FOREGROUND_RGB (FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_GREEN) #define BACKGROUND_RGB (BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_GREEN) #define CHECK_OS(expr) do { if (!(expr)) { fprintf(stderr, "API failed: %s\n", #expr); exit(1); } } while(0) static WORD get_text_attributes(Terminal term) { CONSOLE_SCREEN_BUFFER_INFO c; CHECK_OS(GetConsoleScreenBufferInfo(term.h, &c)); return c.wAttributes; } Terminal getHandle(FILE* in, FILE* out) { Terminal term; if ((in == stdin) && (out == stdout)) { term.h = GetStdHandle(STD_OUTPUT_HANDLE); term.hin = GetStdHandle(STD_INPUT_HANDLE); CHECK_OS(term.h != INVALID_HANDLE_VALUE); CHECK_OS(term.hin != INVALID_HANDLE_VALUE); } else { term.h = (HANDLE)_get_osfhandle(_fileno(out)); term.hin = (HANDLE)_get_osfhandle(_fileno(in)); CHECK_OS(term.h != INVALID_HANDLE_VALUE); CHECK_OS(term.hin != INVALID_HANDLE_VALUE); } term.savedAttr = get_text_attributes(term); return term; } void setFG(Terminal term, int color) { WORD attr = get_text_attributes(term); attr &= ~FOREGROUND_RGB; // 清除原有前景色 attr &= ~FOREGROUND_INTENSITY; // 清除原有前景亮度 attr |= color; CHECK_OS(SetConsoleTextAttribute(term.h, attr)); } void setBG(Terminal term, int color) { WORD attr = get_text_attributes(term); attr &= ~BACKGROUND_RGB; // 清除原有背景色 attr &= ~BACKGROUND_INTENSITY; // 清除原有背景亮度 attr |= color; CHECK_OS(SetConsoleTextAttribute(term.h, attr)); } int main() { Terminal term = getHandle(stdin, stdout); setFG(term, FOREGROUND_RED); DWORD written; CHECK_OS(WriteConsole(term.h, "Hello, ", 7, &written, NULL)); setFG(term, FOREGROUND_BLUE); CHECK_OS(WriteConsole(term.h, "World !\r\n", 9, &written, NULL)); // 恢复原有属性 CHECK_OS(SetConsoleTextAttribute(term.h, term.savedAttr)); return 0; }
修复说明
- 替换
CreateFile("CON")为GetStdHandle,确保获取正确的标准输出/输入句柄。 - 新增
CHECK_OS宏,对所有Win32 API调用添加错误检查,提前发现并报告无效句柄等问题。 - 移除
get_text_attributes中返回固定值的逻辑,API失败时直接终止程序并报错,避免错误属性被使用。 - 为
WriteConsole添加返回值检查,并传递written参数(符合API要求,避免未初始化参数问题)。
内容的提问来源于stack exchange,提问作者WIP
相关产品推荐
相关产品推荐

