当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在C#控制台应用程序中捕获ctrl-c(SIGINT)》经验,为你挑选了5个好方法。

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



1> Jonas..:

该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循环退出.这是一种使程序优雅退出的方法.


`keepRunning`可能需要标记为`volatile`.主线程可能会将其缓存在CPU寄存器上,否则在执行委托时不会注意到值的变化.
应该更改为使用`ManualResetEvent`而不是旋转`bool`.

2> aku..:

请参阅MSDN:

Console.CancelKeyPress事件

包含代码示例的文章:

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


实际上,那篇文章推荐了P/Invoke,并且在评论中只提到了"CancelKeyPress".一篇好文章是http://www.codeneverwritten.com/2006/10/ctrl-c-and-net-console-application.html
@ bzlm评论的非破坏链接:http://web.archive.org/web/20110424085511/http://www.codeneverwritten.com/2006/10/ctrl-c-and-net-console-application.html

3> Jonathon Rei..:

我想补充乔纳斯的回答.在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();
}


我认为关键在于你将在while循环中完成所有工作,并且在迭代过程中按Ctrl + C不会中断; 它会在爆发前完成该迭代.
@ pkr298 - 太糟糕的人不会投票你的评论,因为它完全正确.我将编辑乔纳斯他的答案,以澄清人们思考乔纳森的方式(这本身并不坏,但不是乔纳斯的意思是他的答案)

4> JJ_Coder4Hir..:

这是一个完整的工作示例.粘贴到空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..");
        }
    }
}


因此p/invokes不是跨平台
只有6年了!

5> Paul..:

这个问题非常类似于:

捕获控制台出口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;
        }
    }
}

推荐阅读
手机用户2402851155
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有