[C#] 控制台应用程序退出事件

2022-04-19 14:05:27 浏览数 (1)

在 .NET 中,是否有一种方法(例如事件)来检测控制台应用程序何时退出?你可能需要清理一些线程和 COM 对象.,记录一下信息等等。。。。。。。

比如我的数据采集软件,每次启动和退出时向钉钉推送相关信息。

代码语言:javascript复制
    private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
    {
      //钉钉消息推送
        DingDing.SendText(Environment.MachineName   " 手动关闭 EdgeServices", new List<string> { });
      //  do some work 比如,本地消息记录等。。。。

踩坑过程:

尝试向 Process.GetCurrentProcess.Exited 和 Process.GetCurrentProcess.Disposed 添加处理程序.

也尝试向 Application.ApplicationExit 和 Application.ThreadExit 事件添加处理程序,但它们没有触发.

推荐

使用 ProcessExit 事件 AppDomain:

代码语言:javascript复制
class Program
{
    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.ProcessExit  = new EventHandler(CurrentDomain_ProcessExit);           
        // do some work

    }

    static void CurrentDomain_ProcessExit(object sender, EventArgs e)
    {
        Console.WriteLine("exit");
    }
}

更新

这是一个完整的示例程序,它有一个在单独线程上运行的空"消息泵",它允许用户在控制台中输入退出命令以优雅地关闭应用程序.在 MessagePump 中的循环之后,您可能希望以一种很好的方式清理线程使用的资源.出于以下几个原因,在那里比在 ProcessExit 中这样做更好:

  • 避免交叉线程问题;如果在 MessagePump 线程上创建了外部 COM 对象,则在那里处理它们会更容易.
  • ProcessExit 有时间限制(默认情况下为 3 秒),因此如果清理很耗时,则在该事件处理程序中执行 pefrom 可能会失败.

代码如下:

代码语言:javascript复制
class Program
{
    private static bool _quitRequested = false;
    private static object _syncLock = new object();
    private static AutoResetEvent _waitHandle = new AutoResetEvent(false);

    static void Main(string[] args)
    {
        AppDomain.CurrentDomain.ProcessExit  = new EventHandler(CurrentDomain_ProcessExit);
        // start the message pumping thread
        Thread msgThread = new Thread(MessagePump);
        msgThread.Start();
        // read input to detect "quit" command
        string command = string.Empty;
        do
        {
            command = Console.ReadLine();
        } while (!command.Equals("quit", StringComparison.InvariantCultureIgnoreCase));
        // signal that we want to quit
        SetQuitRequested();
        // wait until the message pump says it's done
        _waitHandle.WaitOne();
        // perform any additional cleanup, logging or whatever
    }

    private static void SetQuitRequested()
    {
        lock (_syncLock)
        {
            _quitRequested = true;
        }
    }

    private static void MessagePump()
    {
        do
        {
            // act on messages
        } while (!_quitRequested);
        _waitHandle.Set();
    }

    static void CurrentDomain_ProcessExit(object sender, EventArgs e)
    {
        Console.WriteLine("exit");
    }
}

其他推荐答案

这是一个完整的、非常简单的 .Net 解决方案,适用于所有版本的 windows.只需将它粘贴到一个新项目中,运行它并尝试使用 CTRL-C 来查看它是如何处理它的:

代码语言:javascript复制
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..");
        }
    }
 }

其他推荐答案

应用程序是一个服务器,它会一直运行到系统关闭或收到 Ctrl C 或控制台窗口关闭为止.

由于应用程序的特殊性,"优雅地"退出是不可行的.(可能我可以编写另一个应用程序来发送"服务器关闭"消息,但这对于一个应用程序来说太过分了,并且在某些情况下仍然不够,例如服务器(实际操作系统)实际关闭时.)

由于这些情况,我添加了一个"ConsoleCtrlHandler" 在那里我停止我的线程并清理我的 COM 对象等...

代码语言:javascript复制
Public Declare Auto Function SetConsoleCtrlHandler Lib "kernel32.dll" (ByVal Handler As HandlerRoutine, ByVal Add As Boolean) As Boolean

Public Delegate Function HandlerRoutine(ByVal CtrlType As CtrlTypes) As Boolean

Public Enum CtrlTypes
  CTRL_C_EVENT = 0
  CTRL_BREAK_EVENT
  CTRL_CLOSE_EVENT
  CTRL_LOGOFF_EVENT = 5
  CTRL_SHUTDOWN_EVENT
End Enum

Public Function ControlHandler(ByVal ctrlType As CtrlTypes) As Boolean
.
.clean up code here
.
End Function

Public Sub Main()
.
.
.
SetConsoleCtrlHandler(New HandlerRoutine(AddressOf ControlHandler), True)
.
.
End Sub

这个设置似乎很完美。

一般情况下使用第一种最简单的方式就可以了。

0 人点赞