代码之家  ›  专栏  ›  技术社区  ›  Statement

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

  •  41
  • Statement  · 技术社区  · 14 年前

    我想知道是否还有别的( 更快 )使用C.NET将文本输出到控制台应用程序窗口的方法比使用简单的 , 背景颜色 底色 方法和属性?我了解到每个单元格都有一个背景色和一个前景色,我希望缓存/缓冲/写入速度比使用上述方法快。

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

    这是一款基于文本的逆向游戏,我想在其中使用标准颜色和ASCII字符来布局游戏。

    请帮助:)

    更新:

    我可能不需要在外面和缓冲区搞混。似乎有一个屏幕缓冲区属于控制台。我不知道如何访问它,也许我只是运气不好,除非我导入一些DLL。

    2 回复  |  直到 7 年前
        1
  •  43
  •   Chris Taylor    14 年前

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

    基本上,如果您获得了控制台缓冲区的句柄,那么您就可以使用标准的win32 apis来操作缓冲区,甚至可以在屏幕外构建整个缓冲区,并将其直接发送到控制台。

    唯一棘手的部分是获取控制台缓冲区的句柄。我没有在.NET中尝试过这个方法,但是在过去的几年中,您可以通过使用createfile(您需要p/invoke这个)和打开“conout$”来获得当前控制台的句柄,然后您可以使用返回到其他API的句柄。

    创建文件的P/Invoke
    http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html

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

    您可以将一个不错的库放在一起,以提供对控制台缓冲区的较低级别访问。

    因为我想让我的.NET重新运行起来,所以我想我会试试这个方法,看看是否能让它正常工作。下面是一个示例,它将在屏幕上填充所有字母a-z,并运行所有地面属性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();
        }
      }
    }  
    
        2
  •  5
  •   adrianbanks    14 年前

    如果你看一下 Console 用于更改控制台颜色的属性,它们委托给 SetConsoleTextAttribute 方法来自 kernel32.dll . 这种方法需要 character attributes 作为输入设置前景和背景色。

    从几个msdn文档页中,每个屏幕缓冲区(控制台有一个)都有一个二维的字符信息记录数组,每个字符信息记录由一个 CHAR_INFO . 这就是决定每个字符颜色的原因。您可以使用 SetConsoleTextAttribute 方法,但这适用于任何写入控制台的新文本-您不能操作控制台上已有的文本。

    除非在控制台文本颜色属性中有一个较低级别的钩子(看起来不太可能),否则我认为您一直在使用这些方法。


    您可以尝试创建一个新的屏幕缓冲区,写入该缓冲区,然后使用 SetConsoleActiveScreenBuffer . 这个 可以 生成更快的输出,因为您将把所有输出写入非活动缓冲区。