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

发布于 2024-07-27 00:47:17 字数 523 浏览 5 评论 0原文

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

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

我尝试向 Process.GetCurrentProcess.Exited 和 Process.GetCurrentProcess.Dispose 添加处理程序。

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

In .NET, is there a method, such as an event, for detecting when a console application is exiting? I need to clean up some threads and COM objects.

I am running a message loop, without a form, from the console application. A DCOM component that I am using seems to require that the application pump messages.

I have tried adding a handler to Process.GetCurrentProcess.Exited and Process.GetCurrentProcess.Disposed.

I have also tried adding a handler to Application.ApplicationExit and Application.ThreadExit events, but they are not firing. Perhaps that is because I am not using a form.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

成熟稳重的好男人 2024-08-03 00:47:17

您可以使用 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");
    }
}

更新

这是一个完整的示例程序,其中一个空的“消息泵”在单独的线程上运行,允许用户在控制台中输入退出命令以正常关闭应用程序。 在 MessagePump 中的循环之后,您可能希望以良好的方式清理线程使用的资源。 这样做比在 ProcessExit 中更好,原因如下:

  • 避免跨线程问题; 如果外部 COM 对象是在 MessagePump 线程上创建的,则在那里处理它们会更容易。
  • 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");
    }
}

You can use the ProcessExit event of the 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");
    }
}

Update

Here is a full example program with an empty "message pump" running on a separate thread, that allows the user to input a quit command in the console to close down the application gracefully. After the loop in MessagePump you will probably want to clean up resources used by the thread in a nice manner. It's better to do that there than in ProcessExit for several reasons:

  • Avoid cross-threading problems; if external COM objects were created on the MessagePump thread, it's easier to deal with them there.
  • There is a time limit on ProcessExit (3 seconds by default), so if cleaning up is time consuming, it may fail if pefromed within that event handler.

Here is the code:

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");
    }
}
夜声 2024-08-03 00:47:17

这是一个完整、非常简单的 .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..");
        }
    }
 }

Here is a complete, very simple .NET solution that works in all versions of Windows. Simply paste it into a new project, run it and try Ctrl + C to view how it handles it:

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..");
        }
    }
 }
自由范儿 2024-08-03 00:47: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

这个设置似乎运行得很完美。 这是 指向相同内容的一些 C# 代码的链接

The application is a server which simply runs until the system shuts down or it receives a Ctrl + C or the console window is closed.

Due to the extraordinary nature of the application, it is not feasible to "gracefully" exit. (It may be that I could code another application which would send a "server shutdown" message, but that would be overkill for one application and still insufficient for certain circumstances like when the server (actually the operating system) is actually shutting down.)

Because of these circumstances I added a "ConsoleCtrlHandler" where I stop my threads and clean up my COM objects, etc...


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

This setup seems to work out perfectly. Here is a link to some C# code for the same thing.

酒绊 2024-08-03 00:47:17

对于 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);
}

For the CTRL+C case, you can use this:

// 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);
}
情魔剑神 2024-08-03 00:47:17

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

If you are using a console application and you are pumping messages, you may be able use the WM_QUIT message.

许你一世情深 2024-08-03 00:47:17

基于@user79755解决方案的最小版本:

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace TestConsoleApp {
    internal class Program {
        private static bool notStoped = true;

        private delegate bool CloseEventHandler(int sig);

        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(CloseEventHandler handler, bool add);

        static void Main(string[] args) {
            SetConsoleCtrlHandler(Handler, true);
            Console.WriteLine("Don't press Enter...");
            Console.WriteLine("Just close the app");
            while (Console.ReadLine() != null);
            //wait for terminate
            do {
                Thread.Sleep(100);
            } while(notStoped);
        }

        protected static bool Handler(int sig) {
            Console.WriteLine("It's closing now");
            Thread.Sleep(2000);
            Console.WriteLine("Almost done");
            Thread.Sleep(1000);
            notStoped = false;
            return true;
        }
    }
}

Minimal version based on @user79755 solution :

using System;
using System.Runtime.InteropServices;
using System.Threading;

namespace TestConsoleApp {
    internal class Program {
        private static bool notStoped = true;

        private delegate bool CloseEventHandler(int sig);

        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(CloseEventHandler handler, bool add);

        static void Main(string[] args) {
            SetConsoleCtrlHandler(Handler, true);
            Console.WriteLine("Don't press Enter...");
            Console.WriteLine("Just close the app");
            while (Console.ReadLine() != null);
            //wait for terminate
            do {
                Thread.Sleep(100);
            } while(notStoped);
        }

        protected static bool Handler(int sig) {
            Console.WriteLine("It's closing now");
            Thread.Sleep(2000);
            Console.WriteLine("Almost done");
            Thread.Sleep(1000);
            notStoped = false;
            return true;
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文