如何将快速彩色输出写入控制台?

Sta*_*ent 43 .net c# console-application

我想学习是否有另一种(更快的)方法使用C#.net将文本输出到控制台应用程序窗口,而不是使用简单的Write,BackgroundColorForegroundColor方法和属性?我了解到每个单元格都有背景颜色和前景色,我想比使用上面提到的方法更快地缓存/缓冲/写入.

也许使用Out缓冲区有一些帮助,但我不知道如何将颜色编码到流中,如果这是颜色数据所在的位置.

这是一个基于retrostyle文本的游戏,我想要实现,我使用标准颜色和ascii字符来布局游戏.

请帮忙 :)

更新:

Out和缓冲区可能不是我需要弄乱的东西.似乎有一个控制台拥有的屏幕缓冲区.我不知道如何访问它,也许我只是运气不好,除非我导入一些dll.

Chr*_*lor 45

更新:添加了一个示例
如果您准备做一些P/Invoke的东西,这可能会有所帮助.

基本上,如果您获得控制台缓冲区的句柄,那么您可以使用标准的Win32 API来操纵缓冲区,甚至可以在屏幕上构建整个缓冲区并将其blit到控制台.

唯一棘手的部分是获取控制台缓冲区的句柄.我没有在.NET中试过这个,但是在过去的几年里,你可以通过使用CreateFile来获取当前控制台的句柄(你需要P/Invoke这个)并打开"CONOUT $"然后你可以使用那个句柄返回传递给其他API.

P/Invoke for CreateFile
http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html

您可以使用WriteConsoleOutput将所有字符及其属性从内存缓冲区移动到控制台缓冲区.
http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx

您可以组合一个很好的库来提供对控制台缓冲区的低级访问.

因为我试图让我的.NET重新开始,所以我想我会尝试一下,看看我是否可以让它工作.这是一个示例,它将用所有字母AZ填充屏幕,并贯穿所有forground属性0-15.我认为你会对表现印象深刻.老实说,我没有花太多时间来检查这段代码,所以错误检查是零,这里或那里可能有一些小错误,但它应该让你继续使用其他的API.

using System;
using System.IO;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace ConsoleApplication1
{
  class Program
  {

    [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    static extern SafeFileHandle CreateFile(
        string fileName,
        [MarshalAs(UnmanagedType.U4)] uint fileAccess,
        [MarshalAs(UnmanagedType.U4)] uint fileShare,
        IntPtr securityAttributes,
        [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition,
        [MarshalAs(UnmanagedType.U4)] int flags,
        IntPtr template);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool WriteConsoleOutput(
      SafeFileHandle hConsoleOutput, 
      CharInfo[] lpBuffer, 
      Coord dwBufferSize, 
      Coord dwBufferCoord, 
      ref SmallRect lpWriteRegion);

    [StructLayout(LayoutKind.Sequential)]
    public struct Coord
    {
      public short X;
      public short Y;

      public Coord(short X, short Y)
      {
        this.X = X;
        this.Y = Y;
      }
    };

    [StructLayout(LayoutKind.Explicit)]
    public struct CharUnion
    {
      [FieldOffset(0)] public char UnicodeChar;
      [FieldOffset(0)] public byte AsciiChar;
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct CharInfo
    {
      [FieldOffset(0)] public CharUnion Char;
      [FieldOffset(2)] public short Attributes;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct SmallRect
    {
      public short Left;
      public short Top;
      public short Right;
      public short Bottom;
    }


    [STAThread]
    static void Main(string[] args)
    {
      SafeFileHandle h = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero);

      if (!h.IsInvalid)
      {
        CharInfo[] buf = new CharInfo[80 * 25];
        SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 };

        for (byte character = 65; character < 65 + 26; ++character)
        {
          for (short attribute = 0; attribute < 15; ++attribute)
          {
            for (int i = 0; i < buf.Length; ++i)
            {
              buf[i].Attributes = attribute;
              buf[i].Char.AsciiChar = character;
            }

            bool b = WriteConsoleOutput(h, buf,
              new Coord() { X = 80, Y = 25 },
              new Coord() { X = 0, Y = 0 },
              ref rect);
          }
        }
      }
      Console.ReadKey();
    }
  }
}  
Run Code Online (Sandbox Code Playgroud)

  • @EthanBierlein,CHAR_INFO结构的Attribute成员在低4位中具有前景颜色,在最低有效字节的高4位中具有背景颜色.例如,如果要将上面的代码更改为具有调色板颜色2的绿色背景,则可以执行以下操作:`buf [i] .Attributes =(short)(attribute |(2 << 4));` (3认同)
  • 实际上,GetStdHandle可以替换对CreateFile的调用. (2认同)

adr*_*nks 5

如果您查看Console用于更改控制台颜色的属性的实现,它们将委托给SetConsoleTextAttribute方法kernel32.dll.此方法将字符属性作为输入来设置前景色和背景色.

在几个MSDN文档页面中,每个屏幕缓冲区(控制台都有一个)具有二维数组信息记录,每个记录由CHAR_INFO表示.这决定了每个角色的颜色.您可以使用该SetConsoleTextAttribute方法对此进行操作,但这适用于写入控制台的任何新文本 - 您无法操作控制台上已有的现有文本.

除非控制台文本颜色属性中有一个较低级别的挂钩(看起来不太可能),否则我认为您使用这些方法时会遇到困难.


您可以尝试的一件事是创建一个新的屏幕缓冲区,写入,然后使用SetConsoleActiveScreenBuffer将其切换为控制台的当前缓冲区.这可能会产生更快的输出,因为您将所有输出写入非活动缓冲区.