2015-02-08 3 views
1

Я пишу игру тетриса C#. Как только я добрался до той части, что приложение было готово. Я добрался до той части, где мне пришлось решить отставание. Я пишу вот так:Pinvoke. Ускорьте Console.Write();

static void writeCol(string a, ConsoleColor b) 
     { 
      ConsoleColor c = Console.ForegroundColor; 
      Console.ForegroundColor = b; 
      Console.Write(a); 
      Console.ForegroundColor = c; 
     } 

Так что, когда новый блок приходит/я хочу двигаться somehing:

writeCol(blokk, ConsoleColor.Magenta); 

Где Blokk является:

private const string blokk = "█"; 

Я нашел способ «быстрее» записать на консоль:

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(); 
    } 
    } 
} 

(Этот код выводит все символы из A-Z). Так finnaly вопрос: Как я могу изменить этот код, чтобы воспользоваться им?

Заранее спасибо. Хорошего дня.

EDIT: Я нашел 1 путь, но это дает мне ошибочный текст. Есть идеи?

public static void Writetocol(string s) 
      { 
       var kiir = s; 
      byte[] barr; 
      kiir = Convert.ToString(kiir); 
      barr = Encoding.ASCII.GetBytes(kiir); 
      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 (short attribute = 0; attribute < 15; ++attribute) 
       { 
        for (int i = 0; i < barr.Length; ++i) 
        { 
         buf[i].Attributes = attribute; 
         buf[i].Char.AsciiChar = barr[i]; 
        } 

        bool b = WriteConsoleOutput(h, buf, 
         new Coord() { X = 80, Y = 25 }, 
         new Coord() { X = 0, Y = 0 }, 
         ref rect); 
       } 
      } 
     } 

Это дает мне это: 1 Когда он должен дать мне это: 2

(Это написано венгерский, если кто-нибудь удивляется)

+5

Вы спрашиваете принципиально неправильный вопрос. То, что вы хотите знать, - это * почему * этот фрагмент быстро и как вы могли его использовать. Если вы эмулируете Console.Write(), то он будет таким же медленным снова. –

+0

Спасибо, я редактировал вопрос. –

+1

Проблема в том, что если вы хотите использовать буфер, вы не можете использовать управляющие символы (которые у вас есть в вашей строке). В элементе буфера массива [0] находится позиция 0,0 на вашем экране. элемент 79 - это позиция 80,0 на экране. элемент 80 - позиция 0,1 (так что это следующая строка). Если ваша программа теперь, по-видимому, предполагает построение строки за строкой (и char by char), вам теперь нужно переосмыслить и реорганизовать экранный подход. – rene

ответ

1

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

static void writeCol(string a, ConsoleColor b) 
    { 
     byte x = 0, y = 0; 
     // parsing to make it fly 
     // fill the buffer with the string 
     for(int ci=0; ci<a.Length;ci++) 
     { 
      switch (a[ci]) 
      { 
       case '\n': // newline char, move to next line, aka y=y+1 
        y++; 
        break; 
       case '\r': // carriage return, aka back to start of line 
        x = 0; 
        break; 
       case ' ': // a space, move the cursor to the right 
        x++; 
        break; 
       default: 
        // calculate where we should be in the buffer 
        int i = y * 80 + x; 
        // color 
        buf[i].Attributes= (short) b; 
        // put the current char from the string in the buffer 
        buf[i].Char.AsciiChar = (byte) a[ci]; 
        x++; 
        break; 
      } 
     } 
     // we handled our string, let's write the whole screen at once 
     bool success = WriteConsoleOutput(h, buf, 
        new Coord() { X = 80, Y = 25 }, 
        new Coord() { X = 0, Y = 0 }, 
        ref rect); 
    } 

Обратите внимание, что я уже переработан в SafeHandle h и родной буфер buf в статическом состоянии, поэтому у нас есть только один раз в приложении:

static IntPtr h= GetStdHandle(STD_OUTPUT_HANDLE); 
static CharInfo[] buf = new CharInfo[80 * 25]; 
static SmallRect rect = new SmallRect() { Left = 0, Top = 0, Right = 80, Bottom = 25 }; 

Я добавил GetStdHandle

//http://www.pinvoke.net/default.aspx/kernel32/GetStdHandle.html 
    const int STD_OUTPUT_HANDLE = -11; 
    [DllImport("kernel32.dll", SetLastError = true)] 
    static extern IntPtr GetStdHandle(int nStdHandle); 

Вам нужно изменить подпись метода на WriteConsoleOutput, чтобы принять вместо SafeFileHandle в этом случае.

Я проверил этот метод со следующей тестового вызова:

writeCol(@" 

    TEST 
    ====== 
    1 test 

    FuBar", ConsoleColor.Blue); 

, который дает этот результат:

enter image description here

Так имейте в виду, чтобы заполнить буфер buf на правильные позиции первого и затем вызовите WriteConsoleOutput, чтобы скопировать буфер на экран одновременно. Если вы называете это очень часто, вы вернетесь к квадрату ...

Обратите внимание, что вам не нужно писать весь экран.Используя разные прямоугольники, вы можете писать только части экрана.

Для этой демонстрации я оставил всю проверку ошибок. Это зависит от вас, чтобы проверить.

Вы можете прочитать на нативных вызовов, используемых с the msdn documentation

+0

Большое спасибо! Просто последнее. Вы знаете, как я могу использовать GetStdHandle для получения буфера, а затем просто передать его мой прямоугольный массив CharInfo, чтобы я мог оставить CreateFile? –

+0

Выполнено, удачи! – rene

+0

Большое спасибо, что вы спасли мой день. –