【问题标题】:SetConsoleMode returning false when enabling ANSI color under Windows 10在 Windows 10 下启用 ANSI 颜色时,SetConsoleMode 返回 false
【发布时间】:2017-11-12 22:19:43
【问题描述】:

我最近将我的一台电脑从 windows 7 pro 升级到 windows 10 pro。我正在尝试运行一些我知道正在另一台 Windows 10 机器上运行的代码(从一开始就是 win10 pro)。在这两种情况下,我都使用 Visual Studio Community 2017,目标平台是 Windows 10,平台工具集是 v141。我尝试了几个不同的 Windows SDK 版本,但并没有改变结果。调试和发布模式构建的行为方式相同。该应用程序是从 Visual Studio 运行的,具有通常的“无需调试即可启动”。从外部控制台运行到 Visual Studio 没有任何区别。

这几乎是 MS 提供的用于在终端应用程序中启用 ANSI 颜色模式的示例代码:

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

int main()
{
  HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
  DWORD consoleMode;
  GetConsoleMode(hConsole, &consoleMode);
  consoleMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;

  if (!SetConsoleMode(hConsole, consoleMode))
  {
    auto e = GetLastError();
    std::cout << "error " << e << "\n";
  }
  else
    std::cout << "\x1b[32mgreen\n";
}

在升级后的机器上,SetConsoleMode 返回 false,GetLastError 返回 87(“参数不正确”),后续输出中控制台不处理 ANSI 颜色代码。在另一台机器上,这一切都成功了,并且按预期呈现了 ansi 颜色。

在升级后的机器上打开控制台窗口显示版本 10.0.10240,在另一台机器上显示版本 10.0.14393。虽然我有点惊讶升级后的机器没有运行更新的版本,但它运行的版本应该支持 ANSI 颜色。

什么可以解释两台机器之间的行为差​​异?

【问题讨论】:

  • 您需要在调用SetConsoleMode 失败后立即调用GetLastError。您的代码之间有很多 C++ 代码,并且不能安全地假设错误代码确实是您声明的错误代码。反正ENABLE_VIRTUAL_TERMINAL_PROCESSING只能用,“如果 hConsoleHandle 参数是屏幕缓冲区句柄”
  • 这与SetConsoleMode 意外失败的事实没有区别,并且在两台机器上运行的相同代码应该显示相同的行为并没有显示任何这样的东西。我对代码进行了简单的编辑,错误代码完全相同,这不足为奇。
  • 不同的是,我们不知道错误模式的细节。它可能是错误代码 87,或者完全是其他东西。当有人指出明显的混乱来源时,无需大惊小怪。
  • 即使根本没有调用GetLastError,相同的代码,在相同的操作系统上以相同的方式构建和运行,在两者上的行为方式应该相同。为什么在一台机器上运行它会提供屏幕缓冲区句柄,而在另一台机器上运行却不提供?

标签: visual-studio winapi windows-console


【解决方案1】:

在 Windows 10 Threshold 2 (TH2)(也称为“11 月更新”,即版本 1511 且内部版本号为 10586)之前,才在控制台窗口中添加对 ANSI 转义序列的支持。

因此,当您将适当的标志传递给SetConsoleMode 时,您的“其他机器”(版本为 10.0.14393)将得到完全支持。但是,版本为 10.0.10240 的“升级机器”无法识别 ENABLE_VIRTUAL_TERMINAL_PROCESSING 标志,因此返回“无效参数”错误。

其实这是officially recommended way to check whether the OS supports the extended console features

以下代码提供了为应用程序启用虚拟终端处理的推荐方法示例。该示例的目的是展示:

  1. 应始终通过GetConsoleMode 检索现有模式并在使用SetConsoleMode 设置之前对其进行分析。
  2. SetConsoleMode 返回 STATUS_INVALID_PARAMETER 是当前确定何时在下层系统上运行的机制。接收到STATUS_INVALID_PARAMETER 的应用程序在位字段中带有较新的控制台模式标志之一,应该优雅地降低行为并重试。

作为下层系统的解决方法,您可以使用ANSIConConEmu

【讨论】:

  • 使用诸如 PDCurses 之类的库或其他抽象 Windows 控制台 API 的库可能比基于 DLL 注入和 API 挂钩的 ANSICON 更好。
  • 这些替代方案有点超出我的专业领域,@eryksun,所以感谢分享。如果您愿意,请随时将指向您描述的项目的链接编辑到答案中,以免错过。
  • 这里是PDCurses 的 GitHub 页面的链接。使用 curses 库是一项重大承诺,但希望得到的回报是更易于跨平台移植的控制台/终端应用程序。
  • 所以,由于 Eryksun 删除了他之前的评论,我显然需要但由于某种原因没有的更新是“创作者更新”,其中包括所有必要的更新并添加了 windows bulid到系统简介页面的编号(解释为什么我以前也找不到)。
猜你喜欢
  • 2017-01-30
  • 2021-01-01
  • 1970-01-01
  • 2021-09-02
  • 2012-01-11
  • 1970-01-01
  • 2021-04-19
  • 1970-01-01
  • 2023-03-19
相关资源
最近更新 更多