Слушайте нажатие клавиши в приложении консоли .NET.
Как я могу продолжать запускать консольное приложение до нажатия клавиши (например, Esc )?
Я предполагаю, что он обернут цикл while. Мне не нравится ReadKey
поскольку он блокирует работу и запрашивает ключ, а не просто продолжает и слушает нажатие клавиши.
Как это может быть сделано?
- Как мне разрешить ввод номера в мое консольное приложение C #?
- Почему закрытие windows консоли сразу после отображения моего вывода?
- Как распечатать UTF-8 из консольного приложения c ++ в Windows
- Как открыть окно консоли
- Как исправить «Нет перегрузки для метода» принимает 0 аргументов »?
- .NET Console TextWriter, который понимает Отступ / Неподвижный / IndentLevel
- Как запустить консольное приложение из Windows Service?
- Какова команда выхода из приложения Console на C #?
- Запустить консольное приложение из другого консольного приложения
- Как остановить автоматическое закрытие приложений C #?
- Как написать быстрый цветной вывод на консоль?
- Как запустить консольное приложение C # с консолью, скрытой
- Установка позиции курсора в консольном приложении Win32
Используйте 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 готов снова прочитать ключи.