我希望能够在C#控制台应用程序中捕获CTRL+ C,以便我可以在退出之前执行一些清理.这样做的最佳方式是什么?
该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) { // Do your work in here, in small chunks. // If you literally just want to wait until ctrl-c, // not doing anything, see the answer using set-reset events. } Console.WriteLine("exited gracefully"); } }
此代码与第一个示例之间的区别在于e.Cancel
设置为true,这意味着在委托之后继续执行.如果运行,程序将等待用户按Ctrl + C.当发生这种情况时,keepRunning
变量会更改值,导致while循环退出.这是一种使程序优雅退出的方法.
请参阅MSDN:
Console.CancelKeyPress事件
包含代码示例的文章:
Ctrl-C和.NET控制台应用程序
我想补充乔纳斯的回答.在a上旋转bool
将导致100%的CPU利用率,并且在等待CTRL+时浪费大量能量做很多事情C.
更好的解决方案是使用a 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的使用.这将导致主线程进入休眠状态,从而释放CPU以在等待退出或清理时处理其他线程.注意:必须在main的末尾设置TerminationCompletedEvent.如果不这样做会导致终止时不必要的延迟,因为在终止应用程序时OS会超时.
namespace CancelSample { using System; using System.Threading; using System.Runtime.InteropServices; internal class Program { ////// Adds or removes an application-defined HandlerRoutine function from the list of handler functions for the calling process /// /// A pointer to the application-defined HandlerRoutine function to be added or removed. This parameter can be NULL. /// If this parameter is TRUE, the handler is added; if it is FALSE, the handler is removed. ///If the function succeeds, the return value is true. [DllImport("Kernel32")] private static extern bool SetConsoleCtrlHandler(ConsoleCloseHandler handler, bool add); ////// The console close handler delegate. /// /// /// The close reason. /// ////// True if cleanup is complete, false to run other registered close handlers. /// private delegate bool ConsoleCloseHandler(int closeReason); ////// Event set when the process is terminated. /// private static readonly ManualResetEvent TerminationRequestedEvent; ////// Event set when the process terminates. /// private static readonly ManualResetEvent TerminationCompletedEvent; ////// Static constructor /// 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); } ////// The main console entry point. /// /// The commandline arguments. 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(); } ////// Method called when the user presses Ctrl-C /// /// The close reason private static bool OnConsoleCloseEvent(int reason) { // Signal termination TerminationRequestedEvent.Set(); // Wait for cleanup TerminationCompletedEvent.WaitOne(); // Don't run other handlers, just exit. return true; } } }