2011-05-05 54 views
165

按键我如何可以继续运行我的控制台应用程序,直到一个按键(如Esc键按下?)聆听到.NET控制台应用程序

我假设它围绕一个while循环包裹。我不喜欢ReadKey,因为它会阻止操作并要求输入密钥,而不是继续听按键。

这怎么办?

回答

251

使用Console.KeyAvailable,所以你只叫ReadKey当你知道它不会阻止:

Console.WriteLine("Press ESC to stop"); 
do { 
    while (! Console.KeyAvailable) { 
     // Do something 
    }  
} while (Console.ReadKey(true).Key != ConsoleKey.Escape); 
+4

但是,如果你这样做而不是readLine(),你失去了通过按“向上”键进行历史回忆的真棒功能。 – 2015-07-01 08:17:09

+2

@ v.oddou在最后一行之后,只需添加你的'Console.ReadLine()',你就被设置了,不是吗? – Gaffi 2016-01-16 01:13:07

61

可以稍微改变你的方法 - 使用Console.ReadKey()停止你的应用程序,但做你的工作在后台线程:

static void Main(string[] args) 
{ 
    var myWorker = new MyWorker(); 
    myWorker.DoStuff(); 
    Console.WriteLine("Press any key to stop..."); 
    Console.ReadKey(); 
} 

myWorker.DoStuff()功能,那么你会调用后台线程的另一个功能(使用Action<>()Func<>()是一个简单的方法来做到这一点),然后立即返回。

12

这里是一个办法为你做在不同的线程东西,并开始听在按下的键不同的线程。当您的实际过程结束或用户通过按Esc键结束过程时,控制台将停止处理。

class SplitAnalyser 
{ 
    public static bool stopProcessor = false; 
    public static bool Terminate = false; 

    static void Main(string[] args) 
    { 
     Console.ForegroundColor = ConsoleColor.Green; 
     Console.WriteLine("Split Analyser starts"); 
     Console.ForegroundColor = ConsoleColor.Red; 
     Console.WriteLine("Press Esc to quit....."); 
     Thread MainThread = new Thread(new ThreadStart(startProcess)); 
     Thread ConsoleKeyListener = new Thread(new ThreadStart(ListerKeyBoardEvent)); 
     MainThread.Name = "Processor"; 
     ConsoleKeyListener.Name = "KeyListener"; 
     MainThread.Start(); 
     ConsoleKeyListener.Start(); 

     while (true) 
     { 
      if (Terminate) 
      { 
       Console.WriteLine("Terminating Process..."); 
       MainThread.Abort(); 
       ConsoleKeyListener.Abort(); 
       Thread.Sleep(2000); 
       Thread.CurrentThread.Abort(); 
       return; 
      } 

      if (stopProcessor) 
      { 
       Console.WriteLine("Ending Process..."); 
       MainThread.Abort(); 
       ConsoleKeyListener.Abort(); 
       Thread.Sleep(2000); 
       Thread.CurrentThread.Abort(); 
       return; 
      } 
     } 
    } 

    public static void ListerKeyBoardEvent() 
    { 
     do 
     { 
      if (Console.ReadKey(true).Key == ConsoleKey.Escape) 
      { 
       Terminate = true; 
      } 
     } while (true); 
    } 

    public static void startProcess() 
    { 
     int i = 0; 
     while (true) 
     { 
      if (!stopProcessor && !Terminate) 
      { 
       Console.ForegroundColor = ConsoleColor.White; 
       Console.WriteLine("Processing...." + i++); 
       Thread.Sleep(3000); 
      } 
      if(i==10) 
       stopProcessor = true; 

     } 
    } 

} 
37

的捷径:

Console.WriteLine("Press ESC to stop"); 

while (!(Console.KeyAvailable && Console.ReadKey(true).Key == ConsoleKey.Escape)) 
{ 
    // do something 
} 

Console.ReadKey()是一个阻塞函数,它停止程序和按键等待的执行,但由于检查Console.KeyAvailable第一,while循环不是阻止,但运行,直到按下Esc

+1

这里可能是最好的答案...非常有帮助谢谢! – 2016-12-30 18:50:14

3

如果您使用Visual Studio,则可以使用“调试”菜单中的“无需调试即可开始”。

它会自动写入“按任意键继续...”在完成应用程序后,您可以将控制台留给您,直到按下某个按键。

13

从C#中的视频诅咒大厦.NET控制台应用程序由贾森·罗伯茨在http://www.pluralsight.com

我们可以做到以下有多个正在运行的进程

static void Main(string[] args) 
    { 
     Console.CancelKeyPress += (sender, e) => 
     { 

      Console.WriteLine("Exiting..."); 
      Environment.Exit(0); 
     }; 

     Console.WriteLine("Press ESC to Exit"); 

     var taskKeys = new Task(ReadKeys); 
     var taskProcessFiles = new Task(ProcessFiles); 

     taskKeys.Start(); 
     taskProcessFiles.Start(); 

     var tasks = new[] { taskKeys }; 
     Task.WaitAll(tasks); 
    } 

    private static void ProcessFiles() 
    { 
     var files = Enumerable.Range(1, 100).Select(n => "File" + n + ".txt"); 

     var taskBusy = new Task(BusyIndicator); 
     taskBusy.Start(); 

     foreach (var file in files) 
     { 
      Thread.Sleep(1000); 
      Console.WriteLine("Procesing file {0}", file); 
     } 
    } 

    private static void BusyIndicator() 
    { 
     var busy = new ConsoleBusyIndicator(); 
     busy.UpdateProgress(); 
    } 

    private static void ReadKeys() 
    { 
     ConsoleKeyInfo key = new ConsoleKeyInfo(); 

     while (!Console.KeyAvailable && key.Key != ConsoleKey.Escape) 
     { 

      key = Console.ReadKey(true); 

      switch (key.Key) 
      { 
       case ConsoleKey.UpArrow: 
        Console.WriteLine("UpArrow was pressed"); 
        break; 
       case ConsoleKey.DownArrow: 
        Console.WriteLine("DownArrow was pressed"); 
        break; 

       case ConsoleKey.RightArrow: 
        Console.WriteLine("RightArrow was pressed"); 
        break; 

       case ConsoleKey.LeftArrow: 
        Console.WriteLine("LeftArrow was pressed"); 
        break; 

       case ConsoleKey.Escape: 
        break; 

       default: 
        if (Console.CapsLock && Console.NumberLock) 
        { 
         Console.WriteLine(key.KeyChar); 
        } 
        break; 
      } 
     } 
    } 
} 

internal class ConsoleBusyIndicator 
{ 
    int _currentBusySymbol; 

    public char[] BusySymbols { get; set; } 

    public ConsoleBusyIndicator() 
    { 
     BusySymbols = new[] { '|', '/', '-', '\\' }; 
    } 
    public void UpdateProgress() 
    { 
     while (true) 
     { 
      Thread.Sleep(100); 
      var originalX = Console.CursorLeft; 
      var originalY = Console.CursorTop; 

      Console.Write(BusySymbols[_currentBusySymbol]); 

      _currentBusySymbol++; 

      if (_currentBusySymbol == BusySymbols.Length) 
      { 
       _currentBusySymbol = 0; 
      } 

      Console.SetCursorPosition(originalX, originalY); 
     } 
    } 
+2

你可以解释一下关于你的方法,我试图用我的控制台应用程序做同样的事情。对代码的解释会很棒。 – 2015-04-29 08:07:31

+2

由国家英里的最佳答案。 – wonea 2017-02-02 16:55:04

+0

@nayefharb设置了一堆任务,启动它们,WaitAll将等待它们全部返回。因此,单个任务将不会返回,并且将一直持续到CancelKeyPress被触发。 – wonea 2017-02-02 16:57:42

-1

根据我的经验,在控制台应用程序最简单的(按箭头键示例):

ConsoleKey readKey = Console.ReadKey().Key; 
if (readKey == ConsoleKey.LeftArrow) { 
    <Method1>(); //Do something 
} else if (readKey == ConsoleKey.RightArrow) { 
    <Method2>(); //Do something 
} 

我用来避免循环,而不是我w在方法中使用上面的代码,我在“Method1”和“Method2”的末尾调用它,因此,在执行“Method1”或“Method2”之后,Console.ReadKey().Key已准备好读取密钥再次。

0

解决的情况下,一些其他的答案不处理好:

  • 响应:按键的处理代码直接执行;避免了轮询或阻塞的变幻莫测
  • 可选性:全球按键是选择加入;否则应用程序应该正常退出
  • 问题分离:侵入性较弱的侦听代码;独立于普通控制台应用代码运行。

许多此页面上的解决方案,涉及轮询Console.KeyAvailable或阻塞Console.ReadKey。尽管.NET Console在这里不是很合作,但您可以使用Task.Run转向更现代的Async收听模式。

主要的问题需要注意的是,默认情况下,您的控制台线程没有设置为Async操作 - 这意味着,当你摔倒了你main功能的底部,而不是等待Async落成,你的AppDoman和过程将结束。解决这个问题的正确方法是使用Stephen Cleary的AsyncContext在您的单线程控制台程序中建立完整的Async支持。但对于更简单的情况,如等待按键,安装完整的trampoline可能会过度。

下面的例子是用于某种迭代批处理文件中的控制台程序。在这种情况下,当程序完成其工作时,通常它应该退出而不需要需要按键,然后我们允许一个可选的按键到阻止该应用程序退出。我们可以暂停周期来检查事情,可能会恢复,或者使用暂停作为已知的“控制点”,以彻底打破批处理文件。

static void Main(String[] args) 
{ 
    Console.WriteLine("Press any key to prevent exit..."); 
    var tHold = Task.Run(() => Console.ReadKey(true)); 

    // ... do your console app activity ... 

    if (tHold.IsCompleted) 
    { 
#if false // For the 'hold' state, you can simply halt forever... 
     Console.WriteLine("Holding."); 
     Thread.Sleep(Timeout.Infinite); 
#else       // ...or allow continuing to exit 
     while (Console.KeyAvailable) 
      Console.ReadKey(true);  // flush/consume any extras 
     Console.WriteLine("Holding. Press 'Esc' to exit."); 
     while (Console.ReadKey(true).Key != ConsoleKey.Escape) 
      ; 
#endif 
    } 
} 
相关问题