如何在C#控制台应用程序中捕获ctrl-c

我希望能够在C#控制台应用程序中捕获CTRL + C ,以便在退出之前执行一些清理操作。 这样做的最好方法是什么?

请参阅MSDN:

Console.CancelKeyPress事件

带代码示例的文章:

Ctrl-C和.NET控制台应用程序

Console.CancelKeyPress事件用于此。 这是如何使用的:

 public static void Main(string[] args) { Console.CancelKeyPress += delegate { // call methods to clean up }; while (true) {} } 

当用户按下Ctrl + C时,委托中的代码被运行,程序退出。 这使您可以通过调用必需的方法来执行清理。 请注意,代理执行后没有任何代码。

还有其他情况下,这不会削减。 例如,如果程序正在执行重要的计算,不能立即停止。 在这种情况下,正确的策略可能是在计算完成后告诉程序退出。 下面的代码给出了一个如何实现的例子:

 class MainClass { private static bool keepRunning = true; public static void Main(string[] args) { Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) { e.Cancel = true; MainClass.keepRunning = false; }; while (MainClass.keepRunning) {} Console.WriteLine("exited gracefully"); } } 

这段代码和第一个例子的区别在于e.Cancel被设置为true,这意味着执行继续在委托之后。 如果运行,程序将等待用户按下Ctrl + C。当发生这种情况时, keepRunningvariables会更改导致while循环退出的值。 这是使程序正常退出的一种方法。

我想补充乔纳斯的答案 。 在bool上旋转会导致100%的CPU利用率,并在等待CTRL + C时浪费一大堆空间。

更好的解决scheme是使用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.."); } } } 

这个问题是非常相似的:

捕获控制台出口C#

下面是我如何解决这个问题,并处理用户击中X以及Ctrl-C。 注意使用ManualResetEvents。 这些将导致主线程hibernate,释放CPU在等待退出或清理时处理其他线程。 注:有必要在主结束时设置TerminationCompletedEvent。 如果不这样做,则由于操作系统超时而导致在终止时造成不必要的延迟,从而导致应用程序死机。

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

Console.TreatControlCAsInput = true; 为我工作