2012-04-26 80 views
1

我有一個控制檯應用程序,提示用戶輸入多個輸入。我希望用戶能夠在任何提示取消操作後按逃生。攔截ESC而無需從緩衝區移除其他按鍵

喜歡的東西:

if (Console.ReadKey().Key != ConsoleKey.Escape) { 
    string input = Console.ReadLine(); 
    ... 
} 

與此問題,但是,如果不是逃跑之外的鍵被按下它會不會是輸入(從ReadLine返回)的一部分。

有沒有辦法「偷看」下一把鑰匙,或者做到這一點?

回答

6
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
internal struct InputRecord 
{ 
    internal short eventType; 
    internal KeyEventRecord keyEvent; 
} 
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] 
internal struct KeyEventRecord 
{ 
    internal bool keyDown; 
    internal short repeatCount; 
    internal short virtualKeyCode; 
    internal short virtualScanCode; 
    internal char uChar; 
    internal int controlKeyState; 
} 

[DllImport("kernel32.dll", SetLastError = true)] 
internal static extern bool PeekConsoleInput(IntPtr hConsoleInput, out InputRecord buffer, int numInputRecords_UseOne, out int numEventsRead); 

[DllImport("kernel32.dll", SetLastError = true)] 
internal static extern bool ReadConsoleInput(IntPtr hConsoleInput, out InputRecord buffer, int numInputRecords_UseOne, out int numEventsRead); 

[DllImport("kernel32.dll", SetLastError = true)] 
internal static extern IntPtr GetStdHandle(int nStdHandle); 

static Nullable<InputRecord> PeekConsoleEvent() 
{ 
    InputRecord ir; 
    int num; 
    if (!PeekConsoleInput(GetStdHandle(-10), out ir, 1, out num)) 
    { 
    //TODO process error 
    } 
    if (num != 0) 
    return ir; 
    return null; 
} 
static InputRecord ReadConsoleInput() 
{ 
    InputRecord ir; 
    int num; 
    if (!ReadConsoleInput(GetStdHandle(-10), out ir, 1, out num)) 
    { 
    //TODO process error 
    } 
    return ir; 
} 
static bool PeekEscapeKey() 
{ 
    for (; ;) 
    { 
    var ev = PeekConsoleEvent(); 
    if (ev == null) 
    { 
     Thread.Sleep(10); 
     continue; 
    } 
    if (ev.Value.eventType == 1 && ev.Value.keyEvent.keyDown && ev.Value.keyEvent.virtualKeyCode == 27) 
    { 
     ReadConsoleInput(); 
     return true; 
    } 
    if (ev.Value.eventType == 1 && ev.Value.keyEvent.keyDown) 
     return false; 
    ReadConsoleInput(); 
    } 
} 

用例:

for (; ;) 
    { 
    Console.Write("?"); 
    if (PeekEscapeKey()) 
    { 
     Console.WriteLine("esc");   
     continue; 
    } 
    Console.Write(">"); 
    var line = Console.ReadLine(); 
    Console.WriteLine(line); 
    } 
+0

我嘗試過'PeekConsoleInput',但顯然有錯誤。你的代碼很棒! – Daniel 2012-04-27 15:28:35

+0

哇,這很相關!我很驚訝這就是它在C#中完成如此簡單的事情所需要的!謝謝(你的)信息! – 2012-09-28 02:24:41

1

要查看,您將不得不使用TextReader.Peek。要在控制檯上使用它,您可以使用Console.In.Peek()。 要使用你所想的代碼:

if(Console.In.Peek() != "\0x1B") // 0x1B is the Escape Key 
{ 
     string input = Console.ReadLine(); 
     .... 
} 

這應該工作。我沒有測試過,但看起來沒錯。

+0

不工作。輸入流中不顯示Escape。 – Daniel 2012-04-27 02:12:32

+0

Console.In是一個TextReader,所以它恢復爲按照它的外觀一次讀取一行文本。 – 2012-09-28 02:17:06

1

或者比這稍微少巧妙的方法@ user823682

ConsoleKeyInfo key = Console.ReadKey().Key; 
if (key != ConsoleKey.Escape) { 
    string input = String.Concat(key.KeyChar,Console.ReadLine()); 
    ... 
} 
+1

在ConsoleKeyInfo中不覆蓋ToString。使用ConsoleKeyInfo.KeyChar獲取被按下的字符可能是一個更好的主意。 – JKor 2012-04-27 00:19:33

+0

鑑於你是對的,我希望這樣做會修改後人。 – 2012-04-27 01:03:58

+0

這不會攔截ESC,因此它會將'←'打印到控制檯。 – Daniel 2012-04-27 02:41:09