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

use*_*755 67 .net complexity-theory console-application

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

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

我已经尝试向Process.GetCurrentProcess.Exited和Process.GetCurrentProcess.Disposed添加一个处理程序.

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

Fre*_*örk 64

您可以使用以下ProcessExit事件AppDomain:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

更新

这是一个完整的示例程序,在单独的线程上运行一个空的"消息泵",允许用户在控制台中输入一个quit命令来正常关闭应用程序.在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");
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 感谢Fredrik的建议.此事件似乎也不会触发. (6认同)
  • 啊......这很有道理; 你没有退出申请,你是用暴力杀死它.没有任何事情可以选择.您需要实现一种优雅地退出应用程序的方法. (3认同)
  • 你是认真的吗?当关闭主窗体的用户关闭任何Windows.Forms应用程序时,您有机会执行一些清理!我正在实施一个批处理,让流程死在我身上是完全不可接受的,甚至没有机会记录工作中止的事实...... (3认同)
  • 这不起作用。将它粘贴到一个新的控制台项目中,解决依赖关系,在 CurrentDomain_ProcessExit 上放置一个断点,并且在按下 CTRL-C 或单击窗口上的 X 时它不会触发。在下面查看我的完整工作回复 (3认同)

JJ_*_*ire 36

这是一个完整的,非常简单的.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..");
        }
    }
 }
Run Code Online (Sandbox Code Playgroud)

  • 这段代码可以在 Linux 上的 .NET Core 控制台应用程序中运行吗?我没有尝试,但“Kernel32”对我来说似乎不可移植...... (6认同)
  • 这似乎是适用于所有退出场景的最佳C#解决方案!完美的工作!谢谢 :) (2认同)
  • 如果用户单击控制台关闭按钮 Thread.Sleep(5000000) 不起作用,控制台将在等待 5 秒后关闭。所以这段代码并不能完全工作。 (2认同)

use*_*755 17

该应用程序是一个服务器,它只运行直到系统关闭或接收到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
Run Code Online (Sandbox Code Playgroud)

这种设置似乎完美无缺.这是一个链接到同一件事的一些C#代码.

  • 处理程序完成似乎有3秒的限制.如果你没有及时完成,你将被非理性地终止.这包括你是否坐在调试器的断点上! (3认同)

Eri*_*Law 8

对于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);
}
Run Code Online (Sandbox Code Playgroud)