首页 文章

在.NET控制台应用程序中监听按键

提问于
浏览
182

我怎样才能继续运行我的控制台应用程序,直到按下按键(如Esc被按下?)

我'm assuming its wrapped around a while loop. I don'喜欢 ReadKey 因为它会阻止操作并要求输入一个键,而不是只是继续并听取按键操作 .

如何才能做到这一点?

9 回答

  • 1

    您可以稍微改变您的方法 - 使用 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<>() 是一种简单的方法),然后立即返回 .

  • 0

    处理其他一些答案处理不当的案例:

    • Responsive :直接执行按键处理代码;避免轮询或阻止延误的变幻莫测

    • Optionality :全球按键是选择加入;否则应用应该正常退出

    • Separation of concerns :侵入性较小的听力代码;独立于正常的控制台应用代码运行 .


    此页面上的许多解决方案都涉及在 Console.ReadKey 上轮询 Console.KeyAvailable 或阻止 . 虽然.NET Console 确实在这里不合作,但你可以使用 Task.Run 来迈向更现代的听力模式 .

    要注意的主要问题是,默认情况下,您的控制台线程未设置为 Async 操作 - 这意味着当您从 main 函数的底部掉出而不是等待 Async 完成时,您的AppDoman和过程将结束 . 解决这个问题的正确方法是使用Stephen Cleary的AsyncContext在您的单线程控制台程序中 Build 完整的 Async 支持 . 但是对于更简单的情况,比如等待按键,安装一个完整的_517208可能有点矫枉过正 .

    下面的示例将用于某种迭代批处理文件中使用的控制台程序 . 在这种情况下,当程序完成其工作时,通常它应该退出 without 需要一个按键,然后我们允许一个可选的按键来退出该应用程序.517210_我们可以暂停循环来检查事物,可能会恢复,或者将暂停用作已知的'control point',以便彻底打破批处理文件 .

    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
        }
    }
    
  • 281

    使用Console.KeyAvailable,以便在您知道它不会阻止时只调用 ReadKey

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

    来自Jason Roberts的视频诅咒在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);
            }
        }
    
  • 3

    这是一种方法,您可以在不同的线程上执行某些操作,并开始侦听在不同线程中按下的键 . 当您的实际流程结束或用户通过按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;
    
            }
        }
    
    }
    
  • -1

    使用下面的代码,您可以按下SpaceBar,在控制台执行中间暂停,直到按下另一个键,并且还会监听EscapeKey以打破主循环

    static ConsoleKeyInfo cki = new ConsoleKeyInfo();
    
    
    while(true) {
          if (WaitOrBreak()) break;
          //your main code
    }
    
     private static bool WaitOrBreak(){
            if (Console.KeyAvailable) cki = Console.ReadKey(true);
            if (cki.Key == ConsoleKey.Spacebar)
            {
                Console.Write("waiting..");
                while (Console.KeyAvailable == false)
                {
                    Thread.Sleep(250);Console.Write(".");
                }
                Console.WriteLine();
                Console.ReadKey(true);
                cki = new ConsoleKeyInfo();
            }
            if (cki.Key == ConsoleKey.Escape) return true;
            return false;
        }
    
  • 70

    根据我的经验,在控制台应用程序中,读取最后一个按键的最简单方法如下(带箭头键的示例):

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

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

  • 46

    最短的方式:

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

    Console.ReadKey() 是一个阻塞函数,它停止执行程序并等待按键,但是由于首先检查 Console.KeyAvailablewhile 循环没有被阻止,而是一直运行直到按下Esc .

  • 12

    如果您使用的是Visual Studio,则可以使用“调试”菜单中的“启动而不调试” .

    它将自动写入“按任意键继续...” . 在完成应用程序后为您提供控制台,它将为您打开控制台,直到按下一个键 .

相关问题