. NET 控制台应用退出事件

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

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

我已经尝试向 Process.GetCurrentProcess.Exted 和 Process.GetCurrentProcess.Dispose 添加一个处理程序。

我还尝试向 应用程序。应用程序出口应用程序事件添加一个处理程序,但它们没有触发。也许这是因为我没有使用表单。

143871 次浏览

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

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

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.

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

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