50 votes

Comment puis-je écrire une sortie colorée rapide dans la console ?

Je veux apprendre s'il existe un autre ( plus rapide ) pour afficher du texte dans la fenêtre de l'application console en utilisant C# .net qu'avec la simple fonction Écrire à , Couleur de fond y ForegroundColor méthodes et propriétés ? J'ai appris que chaque cellule a une couleur d'arrière-plan et une couleur d'avant-plan, et j'aimerais mettre en cache/tampon/écriture plus rapidement qu'en utilisant les méthodes mentionnées.

Il existe peut-être une aide pour utiliser le tampon de sortie, mais je ne sais pas comment coder les couleurs dans le flux, si c'est là que résident les données de couleur.

Il s'agit d'un jeu textuel de style rétro que je souhaite mettre en œuvre et dans lequel j'utilise les couleurs standard et les caractères ascii pour la mise en page du jeu.

Veuillez m'aider :)

Mise à jour :

La sortie et le tampon ne sont probablement pas ce dont j'ai besoin pour jouer avec. Il semble y avoir un tampon d'écran qui appartient à la console. Je ne sais pas comment y accéder, peut-être que je n'ai pas de chance à moins d'importer quelques dlls.

57voto

Chris Taylor Points 25865

Mise à jour : ajout d'un échantillon
Si vous êtes prêt à faire un peu de P/Invoke, cela pourrait vous aider.

En fait, si vous obtenez un identifiant pour le tampon de la console, vous pouvez utiliser les API Win32 standard pour manipuler le tampon, et même construire l'intégralité du tampon en dehors de l'écran et le transmettre à la console.

La seule partie délicate est d'obtenir la poignée du tampon de la console. Je n'ai pas essayé cela dans .NET, mais dans les années passées, vous pouviez obtenir le handle de la console courante en utilisant CreateFile (vous aurez besoin de P/Invoke ceci) et ouvrir "CONOUT$" puis vous pouvez utiliser le handle qui est retourné pour passer aux autres APIs.

P/Invocation pour CreateFile
http://www.pinvoke.net/default.aspx/kernel32/CreateFile.html

Et vous pouvez utiliser WriteConsoleOutput pour déplacer tous les caractères et leurs attributs d'un tampon mémoire vers le tampon de la console.
http://msdn.microsoft.com/en-us/library/ms687404(VS.85).aspx

Vous pourriez probablement mettre en place une belle bibliothèque pour fournir un accès de bas niveau au tampon de la console.

Comme j'essaie de remettre mon .NET à niveau, je me suis dit que j'allais m'y essayer et voir si je pouvais le faire fonctionner. Voici un exemple qui remplira l'écran avec toutes les lettres de A à Z et parcourra tous les attributs de terrain de 0 à 15. Je pense que vous serez impressionné par les performances. Je vais être honnête, je n'ai pas passé beaucoup de temps à réviser ce code, donc la vérification des erreurs est nulle et il pourrait y avoir un petit bug ici ou là, mais cela devrait vous permettre d'utiliser le reste des 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();
    }
  }
}

0 votes

Merci beaucoup pour votre aide ! J'ai réussi à obtenir une sortie de base après quelques manipulations. J'utilise GetStdHandle pour obtenir le tampon, puis je lui passe mon tableau rectangulaire de CharInfo. Je vais maintenant tester le vôtre !

2 votes

En fait, GetStdHandle peut remplacer l'appel à CreateFile.

0 votes

Existe-t-il un moyen de prendre en charge les couleurs d'arrière-plan avec ce système ? Je peux faire des couleurs d'avant-plan très bien en faisant buf[i].Attributes = (short)ConsoleColor.Foreground; . J'ai essayé de faire buf[i].Attributes = (short)ConsoleColor.Foreground | (short)ConsoleColor.Background; mais cela ne semble pas fonctionner.

5voto

adrianbanks Points 36858

Si vous regardez l'implémentation de Console Pour modifier les couleurs de la console, les propriétés de l'utilisateur sont déléguées à l'utilisateur. SetConsoleTextAttribute méthode de kernel32.dll . Cette méthode prend attributs de caractère comme entrée pour définir les couleurs d'avant-plan et d'arrière-plan.

D'après plusieurs pages de la documentation MSDN, chaque tampon d'écran (dont une console) possède un tableau bidimensionnel d'enregistrements d'informations sur les caractères, chacun étant représenté par un fichier de type CHAR_INFO . C'est ce qui détermine la couleur de chaque personnage. Vous pouvez la manipuler à l'aide de la fonction SetConsoleTextAttribute mais cette méthode s'applique à tout nouveau texte écrit sur la console - vous ne pouvez pas manipuler du texte existant déjà sur la console.

À moins qu'il n'y ait un crochet de niveau inférieur dans les propriétés de couleur du texte de la console (ce qui semble peu probable), je pense que vous êtes coincé en utilisant ces méthodes.


Une chose que vous pouvez essayer est de créer un nouveau tampon d'écran, d'écrire dans ce tampon, puis de le changer en tampon courant de la console en utilisant SetConsoleActiveScreenBuffer . Ce site mai produisent une sortie plus rapide car vous écrirez toute la sortie dans un tampon inactif.

0 votes

J'ai essayé d'utiliser SetConsoleActiveScreenBuffer mais j'ai obtenu de mauvais résultats. Le scintillement n'a pas diminué et le temps d'exécution a doublé. Je vérifierai à nouveau mon implémentation plus tard, peut-être y a-t-il un problème avec les paramètres de CreateConsoleScreenBuffer...

2voto

J'ai réussi à utiliser

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

Mais si quelqu'un peut me conseiller sur la façon d'écrire l'ASCII étendu pour cela, je lui en serai reconnaissant.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X