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

时间:2022-11-17 00:11:48

I want to learn if there is another (faster) way to output text to the console application window using C# .net than with the simple Write, BackgroundColor and ForegroundColor methods and properties? I learned that each cell has a background color and a foreground color, and I would like to cache/buffer/write faster than using the mentioned methods.

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

Maybe there is some help using the Out buffer, but I don't know how to encode the colors into the stream, if that is where the color data resides.

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

This is for a retrostyle textbased game I am wanting to implement where I make use of the standard colors and ascii characters for laying out the game.

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

Please help :)

请帮忙 :)

Update:

更新:

The Out and buffer is probably not what I need to mess around with. There seems to be a screen buffer that is owned by the console. I don't know how to access it, maybe I am just out of luck unless I import some dlls.

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

2 个解决方案

#1


41  

Update: added a sample
If you are prepared to do some P/Invoke stuff, this might help.

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

Basically if you get a handle to the console buffer, then you can use the standard Win32 APIs wot manipulate the buffer, even build the the entire buffer off screen and the blit it to the Console.

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

The only tricky part is getting the handle to the console buffer. I have not tried this in .NET, but in years gone by, you could get the handle to the current console by using CreateFile (you will need to P/Invoke this) and open "CONOUT$" then you can use the handle that is return to pass to the other APIs.

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

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

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

And you can use WriteConsoleOutput to move all the characters and their attributes from a memory buffer to the console buffer.
http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx

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

You could probably put together a nice library to provide lower-level access to the console buffer.

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

Since I am trying to get my .NET up to scratch again I thought I would try my hand at this and see if I could get it to work. Here is a sample that will fill the screen with all the letters A-Z and run through all the forground attributes 0-15. I think you will be impressed with the performance. I'll be honest, I did not spend much time reviewing this code so error checking is zero and there might be a little bug here or there but it should get you going with the rest of the APIs.

因为我试图让我的.NET重新开始,所以我想我会尝试一下,看看我是否可以让它工作。这是一个示例,它将用所有字母A-Z填充屏幕,并贯穿所有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();
    }
  }
}  

#2


5  

If you look at the implementation of Console's properties for altering console colours, they delegate to the SetConsoleTextAttribute method from kernel32.dll. This method takes character attributes as input to set both the foreground and background colours.

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

From several MSDN doc pages, each screen buffer (of which a console has one) has a two-dimensional array of character info records, each represented by a CHAR_INFO. This is what determines the colour of each character. You can manipulate this using the SetConsoleTextAttribute method, but this is applied to any new text that is written to the console - you cannot manipulate existing text already on the console.

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

Unless there is a lower-level hook into the console text colour properties (which doesn't look likely), I think you are stuck using these methods.

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


One thing you could try is to create a new screen buffer, write to that, and then switch it to be the console's current buffer using SetConsoleActiveScreenBuffer. This may yield faster output as you will be writing all output to an inactive buffer.

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

#1


41  

Update: added a sample
If you are prepared to do some P/Invoke stuff, this might help.

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

Basically if you get a handle to the console buffer, then you can use the standard Win32 APIs wot manipulate the buffer, even build the the entire buffer off screen and the blit it to the Console.

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

The only tricky part is getting the handle to the console buffer. I have not tried this in .NET, but in years gone by, you could get the handle to the current console by using CreateFile (you will need to P/Invoke this) and open "CONOUT$" then you can use the handle that is return to pass to the other APIs.

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

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

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

And you can use WriteConsoleOutput to move all the characters and their attributes from a memory buffer to the console buffer.
http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx

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

You could probably put together a nice library to provide lower-level access to the console buffer.

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

Since I am trying to get my .NET up to scratch again I thought I would try my hand at this and see if I could get it to work. Here is a sample that will fill the screen with all the letters A-Z and run through all the forground attributes 0-15. I think you will be impressed with the performance. I'll be honest, I did not spend much time reviewing this code so error checking is zero and there might be a little bug here or there but it should get you going with the rest of the APIs.

因为我试图让我的.NET重新开始,所以我想我会尝试一下,看看我是否可以让它工作。这是一个示例,它将用所有字母A-Z填充屏幕,并贯穿所有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();
    }
  }
}  

#2


5  

If you look at the implementation of Console's properties for altering console colours, they delegate to the SetConsoleTextAttribute method from kernel32.dll. This method takes character attributes as input to set both the foreground and background colours.

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

From several MSDN doc pages, each screen buffer (of which a console has one) has a two-dimensional array of character info records, each represented by a CHAR_INFO. This is what determines the colour of each character. You can manipulate this using the SetConsoleTextAttribute method, but this is applied to any new text that is written to the console - you cannot manipulate existing text already on the console.

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

Unless there is a lower-level hook into the console text colour properties (which doesn't look likely), I think you are stuck using these methods.

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


One thing you could try is to create a new screen buffer, write to that, and then switch it to be the console's current buffer using SetConsoleActiveScreenBuffer. This may yield faster output as you will be writing all output to an inactive buffer.

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