2012-04-26 78 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