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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 02:40:18