2015-02-08 79 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問題: 我如何修改此代碼以利用它?

在此先感謝。祝你今天愉快。

編輯: 我發現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(),那麼它將再次變得緩慢。 – 2015-02-08 21:48:41

+0

謝謝我編輯了這個問題。 – 2015-02-08 22:00:58

+1

現在的問題是,如果你想利用緩衝區,你不能使用控制字符(你在你的字符串中)。在數組緩衝區中,元素[0]在屏幕上的位置爲0,0。元素79在屏幕上的位置80,0。元素80的位置是0,1(這是下一行)。如果你的程序現在似乎假設逐行(和char字符)集合,你現在必須重新思考並重構爲逐個屏幕的方法。 – rene 2015-02-08 22:08:47

回答

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上的方法簽名更改爲接受IntPtr而不是SafeFileHandle

我測試了這種方法具有以下測試呼叫:

writeCol(@" 

    TEST 
    ====== 
    1 test 

    FuBar", ConsoleColor.Blue); 

這給出了這樣的結果:

enter image description here

所以請記住在正確的位置上第一填充緩衝區buf和然後撥打WriteConsoleOutput立即將緩衝區複製到屏幕上。如果你經常打電話給你,你回到原來的位置......

注意你不需要寫整個屏幕。通過使用不同的矩形,您可以只寫入部分屏幕。

對於本演示,我忽略了所有錯誤檢查。這是由你來檢查。

你可能想在你問一個根本性的錯誤問題,從the msdn documentation

+0

非常感謝!只是最後一件事。你知道我怎麼可以使用GetStdHandle獲取緩衝區,然後只是將它傳遞給我的矩形CharInfo數組,所以我可以省略CreateFile? – 2015-02-08 22:57:09

+0

完成,祝你好運! – rene 2015-02-08 23:12:45

+0

非常感謝你拯救了我的一天。 – 2015-02-08 23:17:26