Слушайте нажатие клавиши в приложении консоли .NET.

Как я могу продолжать запускать консольное приложение до нажатия клавиши (например, Esc )?

Я предполагаю, что он обернут цикл while. Мне не нравится ReadKey поскольку он блокирует работу и запрашивает ключ, а не просто продолжает и слушает нажатие клавиши.

Как это может быть сделано?

Используйте Console.KeyAvailable чтобы вы только вызывали ReadKey когда знаете, что он не будет блокироваться:

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

Вы можете немного изменить свой подход – используйте Console.ReadKey() чтобы остановить приложение, но выполняйте свою работу в фоновом streamе:

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

В функции myWorker.DoStuff() вы затем вызываете другую функцию в фоновом streamе (с помощью Action<>() или Func<>() это простой способ сделать это), а затем немедленно вернуться.

Самый короткий путь:

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

Console.ReadKey() – это функция блокировки, она останавливает выполнение программы и ждет нажатия клавиши, но благодаря проверке сначала Console.KeyAvailable цикл while не блокируется, а работает до тех пор, пока не будет нажата Esc .

Из видеоролика Building .NET Console Applications в C # Джейсона Робертса на 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); } } 

Здесь вы можете сделать что-то в другом streamе и начать слушать клавишу, нажатую в другом streamе. Консоль прекратит свою обработку, когда ваш фактический процесс завершится, или пользователь завершит процесс нажатием клавиши 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; } } } 

Если вы используете Visual Studio, вы можете использовать «Начать без отладки» в меню «Отладка».

Он автоматически напишет «Нажмите любую клавишу, чтобы продолжить». на консоль для вас по завершении приложения, и он оставит консоль открытой для вас, пока не будет нажата клавиша.

Рассмотрение случаев, когда некоторые из других ответов плохо справляются:

  • Отзывчивое : прямое выполнение кода обработки нажатия клавиши; избегает капризов опроса или блокировки задержек
  • Опциональность : глобальное нажатие кнопки – выбор ; в противном случае приложение должно выйти из системы
  • Разделение проблем : менее инвазивный код прослушивания; работает независимо от обычного кода консольного приложения.

Многие из решений на этой странице include опрос Console.KeyAvailable или блокирование на Console.ReadKey . Console.ReadKey . Хотя это правда, что .NET Console здесь не очень эффективна, вы можете использовать Task.Run для перехода к более современному Async режиму прослушивания.

Основная проблема, о которой следует помнить, заключается в том, что по умолчанию консольный stream не настроен для работы Async а это означает, что, когда вы выходите из нижней части своей main функции, вместо ожидания завершения Async ваши AppDoman и процесс закончится. Правильным способом решения этой проблемы было бы использовать AsyncContext от Stephen Cleary, чтобы установить полную поддержку Async в вашей однопоточной консольной программе. Но для более простых случаев, таких как ожидание нажатия клавиши, установка полного батута может быть чрезмерной.

Пример ниже будет для консольной программы, используемой в каком-то итеративном командном файле. В этом случае, когда программа выполняется с ее работой, обычно она должна выйти, не требуя нажатия клавиши, а затем мы разрешаем необязательное нажатие клавиши для предотвращения выхода приложения. Мы можем приостановить цикл, чтобы исследовать вещи, возможно, возобновить или использовать паузу как известную «контрольную точку», в которой можно полностью вырваться из командного файла.

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

с помощью кода ниже вы можете прослушивать 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; } 

По моему опыту, в консольных приложениях самый простой способ прочитать последнюю нажатую клавишу выглядит следующим образом (Пример со стрелками):

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

Я использую, чтобы избежать циклов, вместо этого я пишу код выше в методе, и я вызываю его в конце «Method1» и «Method2», поэтому после выполнения «Method1» или «Method2» Console.ReadKey () .Key готов снова прочитать ключи.

  • Не удалось загрузить файл или сборку ... Была сделана попытка загрузить программу с неправильным форматом (System.BadImageFormatException)
  • Owin Self host & ASP .Net MVC
  • Невозможно указать модификатор «async» в методе «Основной» консольного приложения
  • как запустить winform из консольного приложения?
  • Отключение ввода в консоли C # до завершения определенной задачи
  • Как получать уведомления устройства Plug & Play без формы windows
  • Как программно отключить режим быстрого редактирования C # консольного приложения?
  • Что происходит, когда вы закрываете консольное приложение c ++
  • Консольное приложение .NET как служба Windows
  • Как запустить консольное приложение .NET в фоновом режиме
  • Давайте будем гением компьютера.