Как я могу записать быстрый цветной вывод на консоль? - PullRequest
41 голосов
/ 02 мая 2010

Я хочу узнать, есть ли другой ( более быстрый ) способ вывода текста в окно консольного приложения с использованием C # .net, чем с простым Запись , BackgroundColor и ForegroundColor методы и свойства? Я узнал, что каждая ячейка имеет цвет фона и цвет переднего плана, и я хотел бы кэшировать / буферизовать / писать быстрее, чем при использовании упомянутых методов.

Может быть, есть некоторая помощь в использовании буфера Out, но я не знаю, как кодировать цвета в поток, если именно там находятся данные о цветах.

Это для текстовой игры в стиле ретро, ​​которую я хочу реализовать, в которой я использую стандартные цвета и символы ascii для выкладки игры.

Пожалуйста, помогите:)

Обновление:

Выход и буфер, вероятно, не то, что мне нужно возиться. Кажется, есть экранный буфер, который принадлежит консоли. Я не знаю, как получить к нему доступ, возможно, мне просто не повезет, если я не импортирую некоторые библиотеки.

Ответы [ 2 ]

43 голосов
/ 02 мая 2010

Обновление: добавлен образец
Если вы готовы сделать кое-что из P / Invoke, это может помочь.

В основном, если вы получите дескриптор буфера консоли, то вы можете использовать стандартные API Win32, чтобы манипулировать буфером, даже построить весь буфер вне экрана и перенаправить его в консоль.

Единственная сложная задача - получить дескриптор буфера консоли. Я не пробовал это в .NET, но в прошедшие годы вы могли получить дескриптор текущей консоли с помощью CreateFile (вам нужно будет P / Invoke this) и открыть «CONOUT $», тогда вы можете использовать дескриптор, который это возврат для передачи другим API.

P / Invoke для CreateFile
http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html

И вы можете использовать WriteConsoleOutput для перемещения всех символов и их атрибутов из буфера памяти в буфер консоли.
http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx

Возможно, вы могли бы собрать хорошую библиотеку, чтобы обеспечить низкоуровневый доступ к буферу консоли.

Поскольку я пытаюсь снова начать работу с .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();
    }
  }
}  
5 голосов
/ 02 мая 2010

Если вы посмотрите на реализацию свойств Console для изменения цветов консоли, они делегируют методу SetConsoleTextAttribute из kernel32.dll. Этот метод принимает символьные атрибуты в качестве входных данных для установки цветов переднего плана и фона.

На нескольких страницах документов MSDN каждый экранный буфер (из которых есть у консоли) имеет двумерный массив символьных информационных записей, каждая из которых представлена ​​ CHAR_INFO . Это то, что определяет цвет каждого персонажа. Вы можете манипулировать этим, используя метод SetConsoleTextAttribute, но это применяется к любому новому тексту, который записывается на консоль - вы не можете манипулировать существующим текстом, уже имеющимся на консоли.

Если только в свойствах цвета текста консоли нет зацепления (что выглядит маловероятно), я думаю, что вы застряли при использовании этих методов.


Одна вещь, которую вы можете попробовать, - создать новый экранный буфер, записать в него, а затем переключиться на текущий буфер консоли, используя SetConsoleActiveScreenBuffer . Это может привести к более быстрому выводу, так как вы будете записывать весь вывод в неактивный буфер.

...