.NET 控制台应用程序退出事件

Posted

技术标签:

【中文标题】.NET 控制台应用程序退出事件【英文标题】:.NET console application exit event 【发布时间】:2010-11-10 07:53:17 【问题描述】:

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

我正在从控制台应用程序运行一个没有表单的消息循环。我正在使用的DCOM 组件似乎要求应用程序泵送消息。

我尝试将处理程序添加到 Process.GetCurrentProcess.Exited 和 Process.GetCurrentProcess.Disposed。

我也尝试向 Application.ApplicationExitApplication.ThreadExit 事件添加处理程序,但它们没有触发。也许那是因为我没有使用表单。

【问题讨论】:

如果您首先设置 Process.EnableRaisingEvents = true,则应该触发 Process.GetCurrentProcess().Exited 事件;否则,它不会触发。 Capture console exit C#的可能重复 @Triynko 怎么样? Process.Exited 在进程退出后被触发。如果退出自己的应用程序,则根本不会执行任何代码。很没用的imo.. 用 C# 完成这篇文章结尾附近的完整工作示例 ***.com/questions/4646827/… 【参考方案1】:

你可以使用AppDomainProcessExit事件:

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 秒),因此如果清理很耗时,则在该事件处理程序中执行可能会失败。

代码如下:

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");
    

【讨论】:

感谢弗雷德里克的建议。此事件似乎也不会触发。 你的进程是如何退出的?您是否调用任何使其退出的代码? 啊.. 有道理;您没有退出应用程序,而是用蛮力杀死它。没有任何事件可以解决这个问题。您需要实现一种优雅退出应用程序的方法。 你是认真的吗?当然,当用户关闭主窗体关闭任何 Windows.Forms 应用程序时,您就有机会执行一些清理工作!我正在实施一个批次,完全不能接受让进程死在我身上,甚至没有机会记录作业被中止的事实...... 这不起作用。将它粘贴到一个新的控制台项目中,解决依赖关系,在 CurrentDomain_ProcessExit 上放置一个断点,当按下 CTRL-C 或单击窗口上的 X 时它不会触发。请参阅下面的完整工作回复【参考方案2】:

这是一个完整、非常简单的 .NET 解决方案,适用于所有版本的 Windows。只需将它粘贴到一个新项目中,运行它并尝试 Ctrl + 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# 解决方案!完美运行!谢谢:) 该代码在 windows xp sp3 中不起作用。如果我们从任务管理器中关闭应用程序。 这段代码能否在 Linux 上的 .NET Core 控制台应用程序中运行?我没试过,但“Kernel32”对我来说似乎不便携...... 我用这个成功了。但是,我使用 ManualResetEvent 来指示何时开始关闭。 (而另一个 ManualResetEvent 在 Handler 中等待,以便在关闭完成时发出信号。) 如果用户单击控制台关闭按钮 Thread.Sleep(5000000) 不起作用,控制台将在等待 5 秒后关闭。所以这段代码并不完全有效。【参考方案3】:

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

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

由于这些情况,我添加了一个“ConsoleCtrlHandler”来停止我的线程并清理我的 COM 对象等...


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

这个设置似乎很完美。这是 a link 一些 C# 代码的同一件事。

【讨论】:

谢谢,它帮助了我 :) +1 您应该将自己的答案标记为答案。 处理程序完成似乎有 3 秒的限制。如果你没有按时完成,你将被无情地终止。这包括您是否位于调试器中的断点上! 这可行,但在下面的 C# 中可以找到完整的工作示例 如果您在任务管理器中使用结束进程,则不会触发此代码。 @dan-gph 程序在主线程完成时终止。添加类似 //hold the console 这样它就不会运行结束 while(!exitSystem) Thread.Sleep(500); 【参考方案4】:

对于 CTRL+C 的情况,你可以这样:

// Tell the system console to handle CTRL+C by calling our method that
// gracefully shuts down.
Console.CancelKeyPress += new ConsoleCancelEventHandler(Console_CancelKeyPress);


static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)

            Console.WriteLine("Shutting down...");
            // Cleanup here
            System.Threading.Thread.Sleep(750);

【讨论】:

这是否处理关机注销 ctrl+c 等...? 试过了,事件永远不会触发。与其他事件相同。【参考方案5】:

如果您正在使用控制台应用程序并且正在发送消息,则可以使用 WM_QUIT 消息。

【讨论】:

我也想知道。我想 可能 是 Windows 不会将消息发送到控制台应用程序的情况,但这对我来说似乎很奇怪。人们应该认为要求是有一个消息泵,而不是它是否有一个 GUI......

以上是关于.NET 控制台应用程序退出事件的主要内容,如果未能解决你的问题,请参考以下文章

如何在 .NET 中指定控制台应用程序的退出代码?

控制台上的陷阱退出

在 C# 中退出控制台应用程序的命令是啥?

asp.net session过期事件

我如何知道Windows何时进入/退出睡眠模式或休眠模式?

System.Environment.Exit(0) 不退出程序