【问题标题】:Unpredictable behavior with Ruby VT100 escape sequences on Windows 10Windows 10 上 Ruby VT100 转义序列的不可预测行为
【发布时间】:2017-07-13 00:03:39
【问题描述】:

在 Windows 10 版本 10.0.14393 上使用 ruby 2.3.1p112 (2016-04-26 revision 54768) [x64-mingw32]

先说几件事:

  1. Windows echo 命令的行为绕过了 VT100 的控制台模式标志。根据 MSDN,这是正常的,其中该标志仅影响 WriteConsole()WriteFile()
  2. 当我用SetConsoleMode() 更改标志时,Win32 函数WriteConsole() 工作正常。设置标志时,它会解释 VT100 转义序列。

那么,Ruby 发生了什么?它以绿色显示红色并以某种方式忽略了我的控制台标志。另外为什么它显示为深绿色?我的理论是,这是 Ruby 以及它如何处理将输出写入控制台的问题。

完整脚本:

#!/usr/bin/ruby
# encoding: UTF-8

require 'rbconfig'

unless RbConfig::CONFIG['host_os'] =~ /mswin|mingw|cygwin/
  raise 'This script only works on Windows. Quitting.'
end

require 'fiddle'
require 'fiddle/types'
require 'fiddle/import'

class VirtMode
  #TODO: Check Windows 10 build number (>= 1511) for mode support
  module Kernel32
    extend Fiddle::Importer
    dlload 'kernel32'
    include Fiddle::Win32Types

    DWORD_SIZE = sizeof('DWORD')
    STD_OUTPUT_HANDLE = -11
    STD_INPUT_HANDLE = -10
    VIRTUAL_TERMINAL_PROCESSING = 0x0004

    extern 'HANDLE GetStdHandle(DWORD)'
    extern 'DWORD SetConsoleMode(HANDLE, DWORD)'
    extern 'DWORD GetConsoleMode(HANDLE, PDWORD)'
    extern 'BOOL WriteConsole(HANDLE, const *char, DWORD, PDWORD, PVOID)'
  end

  class << self; attr_accessor :stdout, :stdin end
  self.stdout = Kernel32::GetStdHandle(Kernel32::STD_OUTPUT_HANDLE)
  self.stdin = Kernel32::GetStdHandle(Kernel32::STD_INPUT_HANDLE)

  def self.get_mode
    mode = [0].pack('L')
    success = Kernel32::GetConsoleMode(stdout, mode)
    return mode.unpack('L').first if success.nonzero?
    raise 'Could not get console mode'
  end

  def self.enable_virtual_mode
    new_mode = get_mode | Kernel32::VIRTUAL_TERMINAL_PROCESSING
    #puts new_mode.to_s(2).rjust(32, '0')
    return Kernel32::SetConsoleMode(stdout, new_mode).nonzero?
  end

  def self.disable_virtual_mode
    new_mode = get_mode & ~Kernel32::VIRTUAL_TERMINAL_PROCESSING
    #puts new_mode.to_s(2).rjust(32, '0')
    return Kernel32::SetConsoleMode(stdout, new_mode).nonzero?
  end

  def self.write_console(text)
    written = 0
    Kernel32::WriteConsole(stdout, text, text.size, written, 0)
  end
end

# It's already disabled but just in case
VirtMode.disable_virtual_mode
puts '--VT100 mode disabled--'
puts "\e[38;2;255;0;32mRuby: Red!\e[0m"
VirtMode.write_console "\e[38;2;255;0;32mWin32: Red!\e[0m\n"
system "echo \e[38;2;255;0;32mEcho: Red!\e[0m\n"

# Now we enable Windows 10 support for VT100
# https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
VirtMode.enable_virtual_mode
puts '--VT100 mode enabled--'
puts "\e[38;2;0;255;32mRuby: Green!\e[0m"
VirtMode.write_console "\e[38;2;0;255;32mWin32: Green!\e[0m\n"
system "echo \e[38;2;0;255;32mEcho: Green!\e[0m\n"

Windows PowerShell 中的示例输出:

请勿在 Rubymine 中调试此脚本,除非您已将其输出到 Windows 控制台,例如 PowerShell 或命令提示符,因为 GetConsoleMode() 将失败。

【问题讨论】:

    标签: ruby windows winapi console vt100


    【解决方案1】:

    2016 年 3 月 8 日,commit 添加了对 Windows 10 或更高版本下本机 VT100 的 Ruby 支持。在此之前,Ruby 使用自己的 VT100 转义序列解析器。

    我安装的 Ruby 版本缺少此更改,因此我选择了更新版本,ruby 2.4.1p111 (2017-03-22 revision 58053) [x64-mingw32]

    自那次提交以来,Ruby 现在做了以下事情:

    1. 如果控制台模式缺少 ENABLE_VIRTUAL_TERMINAL_PROCESSING 标志,则使用自己的 VT100 解析器。
    2. 如果存在该标志,则使用 Windows VT100 支持。

    Ruby 将绿色显示为红色的原因可能是 Ruby 的 VT100 RGB 颜色代码解析器中的错误。例如,"\e[38;2;255;0;32mRuby: Red!\e[0m" 有红色 (255),它实际上将转义序列解释为绿色的"\e[32mRuby: Red!\e[0m"。此错误还解释了启用 VT100 模式时的颜色差异。

    使用 Windows VT 模式的 Ruby 2.4 可以正确显示绿色。原来 Ruby 不解释 RGB 颜色代码,因为 RGB 是一些虚拟终端支持的扩展(感谢 Thomas-Dickey)。

    【讨论】:

    • 实际上,Ruby 是正确的(对于 VT100,它从未做过 RGB)。导致混淆的分号分隔符问题已在其他地方进行了详细讨论(常见问题解答)。
    • 哦,我完全错过了。现在有道理了。努力寻找该讨论,您有常见问题解答的链接吗?
    • 启动here 并连接到here, too。虽然这是一个大约十年来的已知问题,但这是第一个应用程序崩溃的报告。
    猜你喜欢
    • 1970-01-01
    • 2011-12-05
    • 2014-09-22
    • 1970-01-01
    • 2017-12-15
    • 2011-12-13
    • 1970-01-01
    • 2016-12-11
    • 2015-09-30
    相关资源
    最近更新 更多