【问题标题】:Is there a way to output ANSI escape sequences using low level output?有没有办法使用低级输出输出 ANSI 转义序列?
【发布时间】:2019-08-28 20:48:19
【问题描述】:

我正在尝试在控制台中制作图形引擎。我不使用任何 GL,但很快希望开始。它是用 C 编写的,就目前而言,它能够构建彩色半 3D 环境,但我发现 win cmd 支持 True color scheme,使用 ANSI 转义序列。

在我对这个主题的研究过程中,我发现了WriteFile(),所有的 printfs 和 putcs 都基于 Win10。但是对于它不提供的所有商品,它仍然很慢。如内部代码所示,writefile本质上是基于这段代码的:

NtWriteFile:

mov r10,rcx 
mov eax,8 
test byte ptr [7FFE0308h],1 
jne NtWriteFile+15h (07FFBA80CAAA5h) 
syscall                                 
ret

这很好,但速度慢且效率低。 我的意思是:我将控制台的输出句柄传递给WriteFile() 和缓冲区,但是对于 160*60 的屏幕(考虑到每个字符都有自己的 ESC[48;2;r;g; bm,20 字节长)我们得到 192000 字节。系统调用的执行时间为 84ms

然后我用WriteConsoleOutputW() 回到我之前的设置。它使用与控制台相同的输出 ptr,但这次是 320*84 的缓冲区(它使用 CHAR_INFO ptr 作为缓冲区,所以每个 4 字节)-107 520 字节它只持续 1ms

WriteConsoleOutputW() 基于相似的系统调用代码:

NtDeviceIoControlFile:

mov r10,rcx 
mov eax,7                           ; notice here is the 7 instead of 8 
test byte ptr [7FFE0308h],1 
jne NtDeviceIoControlFile+15h (07FFBA80CAAA5h) 
syscall                                 
ret

但这次它输出的信息量大致相同,但速度快了 80 倍!我的假设,它只是重新分配一个指向控制台缓冲区的指针,因为当我用单个字符的缓冲区启动它时,它会输出一些带有颜色的奇怪符号

我尝试将 ESC 序列输出为CHAR_INFO,但它会将它们输出为文本。所以问题是:我可以使用 WriteConsoleOutput 或NtDeviceIoControlFile() 以某种方式输出转义序列吗?

另外:我的帧缓冲区将仅包含转义序列和其后的空格符号。定义了缓冲区的宽度和高度,以及一个矩形,我需要做的就是在每个空间上输出正确的前景色。(所以长度将是 20 * 高度 * 宽度)

【问题讨论】:

  • As internal code shows, writefile is essentially based on this code:WriteConsoleOutputW() is based on similar looking syscall code::看这段代码毫无意义。 “魔法”发生在syscall 被命中时,它会跳转到真正代码所在的内核。
  • @tkausl 我知道,但是所有这些函数都可以从代码中调用并有它们的文档,但是如果读取了 writefile esc,那么第二个 bun 就不会了。我想展示的是缓慢在哪里
  • 您是如何衡量这些时间的?你怎么知道操作系统没有偷窥你的任务?
  • @PaulOgilvie 有 2 个测量值:1) Visual Studio 2015 的内部调试工具,2) 包装函数上的 QueryPerformanceCounter(它们看起来很相似)
  • 据记录,VT 支持仅针对高级流函数WriteFile(即 Windows 8+ 中的NtWriteFile)实现,但系统调用是实现细节;在 Windows 7 中有所不同) 和WriteConsole(即Windows 8+ 中的NtDeviceIoControlFile,用于屏幕缓冲区文件,使用未记录的IOCTL 代码)。低级控制台 API 是基于字符单元的而不是流式的,因此它与虚拟终端接口不兼容。字符单元 API 性能更好,但代价是失去了高级处理。

标签: c windows cmd system-calls ansi-escape


【解决方案1】:

CMD 是一个 VT100 模拟器(该信息来自 Telnet 服务器应用程序帮助)。

我不知道你为什么一直在谈论 ANSI 和没有特性的函数。

不支持 ANSI,但最新版本的 Windows 10 除外。

不使用ANSI 使用SetConsoleTextAttribute(表示颜色)。还有其他功能可以移动光标。

    Dim hOut as IntPtr
    Dim Ret as Integer
    hOut  = GetStdHandle(STD_OUTPUT_HANDLE)
    Ret = SetConsoleTextAttribute(hOut,  &hfA)
    Console.Out.Write("*")
    Ret = SetConsoleTextAttribute(hOut, &hfC)
    Console.Out.Write("WARNING")
    Ret = SetConsoleTextAttribute(hOut, &hfA)
    Console.Out.Write("*" & vbcrlf)

移动光标使用

Pos.X = CInt(Xpos)
Pos.Y= CInt(Ypos)
Ret = SetConsoleCursorPosition(hOut,  Pos)

writefile 的行为(并且有三种写法)由SetConsoleMode 控制(也可以打开 ANSI)。见https://docs.microsoft.com/en-us/windows/console/setconsolemode

两个高级模式是writefilewriteconsole。低级是writeconsoleoutputcharacter

【讨论】:

  • CMD 是一种使用标准 I/O 的 shell 和脚本语言。它不是控制台或终端,也绝对不是虚拟终端模拟器。默认情况下,它为标准 I/O 使用控制台,如果它不继承一个控制台,它将分配它,就像任何其他控制台应用程序一样。
  • Telnet 进入 Windows,看看你得到了什么 shell。
  • shell 既不是终端/控制台也不是 telnet/ssh 服务器。如果我们 telnet 或 ssh 进入 Windows 7/8 系统,服务器必须分配一个控制台并生成目标程序(任何基于控制台的应用程序,而不仅仅是 cmd.exe),以便它继承控制台。然后它必须轮询并读取屏幕缓冲区并将其转换为可由客户端终端解释的流。谢天谢地,Windows 10 支持伪控制台。现在服务器可以调用 CreatePseudoConsole 来获取 VT 流,而不是通过低效的轮询从头开始实现。
猜你喜欢
  • 2014-05-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-26
  • 1970-01-01
  • 1970-01-01
  • 2016-10-20
相关资源
最近更新 更多