Как перехватить ctrl-c в консольном приложении C#
Я хотел бы иметь возможность ловушки CTRL+C в консольном приложении C#, чтобы я мог выполнить некоторые очистки перед выходом. Каков наилучший способ сделать это?
6 ответов:
Я хотел бы добавить к Джонас ответа. Вращаясь на
bool
вызовет 100% загрузку процессора, и тратить кучу энергии делать много ничего, ожидая CTRL+C.лучшее решение-использовать
ManualResetEvent
на самом деле "ждать"CTRL+C:static void Main(string[] args) { var exitEvent = new ManualResetEvent(false); Console.CancelKeyPress += (sender, eventArgs) => { eventArgs.Cancel = true; exitEvent.Set(); }; var server = new MyServer(); // example server.Run(); exitEvent.WaitOne(); server.Stop(); }
вот полный рабочий пример. вставить в пустой консольный проект C#:
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; namespace TestTrapCtrlC { public class Program { static bool exitSystem = false; #region Trap application termination [DllImport("Kernel32")] private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); private delegate bool EventHandler(CtrlType sig); static EventHandler _handler; enum CtrlType { CTRL_C_EVENT = 0, CTRL_BREAK_EVENT = 1, CTRL_CLOSE_EVENT = 2, CTRL_LOGOFF_EVENT = 5, CTRL_SHUTDOWN_EVENT = 6 } private static bool Handler(CtrlType sig) { Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown"); //do your cleanup here Thread.Sleep(5000); //simulate some cleanup delay Console.WriteLine("Cleanup complete"); //allow main to run off exitSystem = true; //shutdown right away so there are no lingering threads Environment.Exit(-1); return true; } #endregion static void Main(string[] args) { // Some biolerplate to react to close window event, CTRL-C, kill, etc _handler += new EventHandler(Handler); SetConsoleCtrlHandler(_handler, true); //start your multi threaded program here Program p = new Program(); p.Start(); //hold the console so it doesn’t run off the end while (!exitSystem) { Thread.Sleep(500); } } public void Start() { // start a thread and start doing some processing Console.WriteLine("Thread started, processing.."); } } }
этот вопрос очень похож на:
вот как я решил эту проблему, и имел дело с пользователем, нажимая X, а также Ctrl-C. обратите внимание на использование ManualResetEvents. Это приведет к тому, что основной поток будет спать, что освобождает процессор для обработки других потоков, ожидая выхода или очистки. Примечание: необходимо установить TerminationCompletedEvent в конце main. Неспособность сделать это приводит к ненужным задержка в завершении из-за тайм-аута ОС при убийстве приложения.
namespace CancelSample { using System; using System.Threading; using System.Runtime.InteropServices; internal class Program { /// <summary> /// Adds or removes an application-defined HandlerRoutine function from the list of handler functions for the calling process /// </summary> /// <param name="handler">A pointer to the application-defined HandlerRoutine function to be added or removed. This parameter can be NULL.</param> /// <param name="add">If this parameter is TRUE, the handler is added; if it is FALSE, the handler is removed.</param> /// <returns>If the function succeeds, the return value is true.</returns> [DllImport("Kernel32")] private static extern bool SetConsoleCtrlHandler(ConsoleCloseHandler handler, bool add); /// <summary> /// The console close handler delegate. /// </summary> /// <param name="closeReason"> /// The close reason. /// </param> /// <returns> /// True if cleanup is complete, false to run other registered close handlers. /// </returns> private delegate bool ConsoleCloseHandler(int closeReason); /// <summary> /// Event set when the process is terminated. /// </summary> private static readonly ManualResetEvent TerminationRequestedEvent; /// <summary> /// Event set when the process terminates. /// </summary> private static readonly ManualResetEvent TerminationCompletedEvent; /// <summary> /// Static constructor /// </summary> static Program() { // Do this initialization here to avoid polluting Main() with it // also this is a great place to initialize multiple static // variables. TerminationRequestedEvent = new ManualResetEvent(false); TerminationCompletedEvent = new ManualResetEvent(false); SetConsoleCtrlHandler(OnConsoleCloseEvent, true); } /// <summary> /// The main console entry point. /// </summary> /// <param name="args">The commandline arguments.</param> private static void Main(string[] args) { // Wait for the termination event while (!TerminationRequestedEvent.WaitOne(0)) { // Something to do while waiting Console.WriteLine("Work"); } // Sleep until termination TerminationRequestedEvent.WaitOne(); // Print a message which represents the operation Console.WriteLine("Cleanup"); // Set this to terminate immediately (if not set, the OS will // eventually kill the process) TerminationCompletedEvent.Set(); } /// <summary> /// Method called when the user presses Ctrl-C /// </summary> /// <param name="reason">The close reason</param> private static bool OnConsoleCloseEvent(int reason) { // Signal termination TerminationRequestedEvent.Set(); // Wait for cleanup TerminationCompletedEvent.WaitOne(); // Don't run other handlers, just exit. return true; } } }