代码之家  ›  专栏  ›  技术社区  ›  Nick Randell

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

  •  194
  • Nick Randell  · 技术社区  · 17 年前

    我想能设陷阱 CTRL + C 在C控制台应用程序中,以便在退出前执行一些清理。最好的方法是什么?

    6 回复  |  直到 8 年前
        1
  •  104
  •   aku    17 年前
        2
  •  201
  •   Ryan Pavlik    8 年前

    这个 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循环退出的值。这是使程序正常退出的一种方法。

        3
  •  80
  •   Mwiza Nagarjuna Durgam    8 年前

    我想补充一下 Jonas' answer . 纺纱 bool 会造成100%的CPU利用率,浪费大量的能量,在等待的时候什么也不做 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();
    }
    
        4
  •  20
  •   JJ_Coder4Hire    12 年前

    下面是一个完整的工作示例。粘贴到空的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..");
            }
        }
    }
    
        5
  •  6
  •   Community Mohan Dere    9 年前

    这个问题非常类似于:

    Capture console exit C#

    下面是我如何解决这个问题,以及如何处理用户点击x和ctrl-c的问题。请注意使用manualReseteEvents。这将导致主线程休眠,从而在等待退出或清理时释放CPU处理其他线程的空间。注意:有必要在main结尾处设置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;
            }
        }
    }
    
        6
  •  3
  •   Community Mohan Dere    8 年前

    Console.TreatControlCAsInput = true; 为我工作。

    推荐文章