web-dev-qa-db-ja.com

高速カラー出力をコンソールに書き込むにはどうすればよいですか?

単純な書き込み、-よりもC#.netを使用してコンソールアプリケーションウィンドウにテキストを出力する別の(高速)方法があるかどうかを知りたいです。 BackgroundColorおよびForegroundColorメソッドとプロパティ?各セルには背景色と前景色があることを学びました。前述の方法を使用するよりも速くキャッシュ/バッファリング/書き込みを行いたいと思います。

Outバッファーを使用するのに役立つかもしれませんが、色データが存在する場所に色をストリームにエンコードする方法がわかりません。

これは、ゲームのレイアウトに標準の色とASCII文字を使用して実装したいレトロスタイルのテキストベースのゲーム用です。

助けてください :)

更新:

アウトとバッファは、おそらく私がいじくり回す必要があるものではありません。コンソールが所有する画面バッファがあるようです。アクセス方法がわからないので、dllをインポートしないと運が悪いかもしれません。

45
Statement

更新: サンプルを追加しました
P/Invokeを実行する準備ができている場合は、これが役立つ可能性があります。

基本的に、コンソールバッファーへのハンドルを取得した場合は、標準のWin32 APIを使用してバッファーを操作し、バッファー全体を画面外に構築してコンソールにブリットすることもできます。

唯一注意が必要なのは、コンソールバッファへのハンドルを取得することです。私は.NETでこれを試していませんが、数年前に、CreateFileを使用して現在のコンソールへのハンドルを取得し(これをP/Invokeする必要があります)、「CONOUT $」を開くと、他のAPIに渡すために戻ります。

P/CreateFileの呼び出し
http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html

また、WriteConsoleOutputを使用して、すべての文字とその属性をメモリバッファからコンソールバッファに移動できます。
http://msdn.Microsoft.com/en-us/library/ms687404(VS.85).aspx

おそらく、Niceライブラリをまとめて、コンソールバッファへの低レベルのアクセスを提供することができます。

.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();
    }
  }
}  
49
Chris Taylor

コンソールの色を変更するためのConsoleのプロパティの実装を見ると、それらはkernel32.dllから SetConsoleTextAttribute メソッドに委任されています。このメソッドは、前景色と背景色の両方を設定するための入力として 文字属性 を取ります。

いくつかのMSDNドキュメントページから、各画面バッファー(コンソールに1つあります)には、文字情報レコードの2次元配列があり、それぞれが CHAR_INFO で表されます。これが各キャラクターの色を決定するものです。これはSetConsoleTextAttributeメソッドを使用して操作できますが、これはコンソールに書き込まれる新しいテキストに適用されます。コンソールに既に存在する既存のテキストを操作することはできません。

コンソールのテキストの色のプロパティに下位レベルのフックがない限り(そうは思われません)、これらの方法を使用して立ち往生していると思います。


試すことができる1つのことは、新しい画面バッファーを作成し、それに書き込み、 SetConsoleActiveScreenBuffer を使用してコンソールの現在のバッファーに切り替えることです。このmayは、すべての出力を非アクティブなバッファーに書き込むため、より高速な出力を生成します。

5
adrianbanks

私は使用して成功しました

using (var stdout = Console.OpenStandardOutput(Cols * Rows))
{
    // fill
    stdout.Write(buffer, 0, buffer.Length);
    // rinse and repeat
}

しかし、誰かが私が拡張ASCIIこれにどのように書くかについて私にアドバイスすることができれば、私は感謝するでしょう

0