.NET 控制台应用程序退出事件
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1119841/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
.NET Console Application Exit Event
提问by user79755
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.
在 .NET 中,是否有一种方法(例如事件)用于检测控制台应用程序何时退出?我需要清理一些线程和 COM 对象。
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.
我正在从控制台应用程序运行一个没有表单的消息循环。我正在使用的 DCOM 组件似乎需要应用程序泵消息。
I have tried adding a handler to Process.GetCurrentProcess.Exited and Process.GetCurrentProcess.Disposed.
我曾尝试向 Process.GetCurrentProcess.Exited 和 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.
我还尝试向 Application.ApplicationExit 和 Application.ThreadExit 事件添加处理程序,但它们没有触发。也许那是因为我没有使用表格。
回答by Fredrik M?rk
You can use the ProcessExitevent of the AppDomain:
您可以使用以下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");
}
}
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:
这是一个完整的示例程序,它有一个在单独线程上运行的空“消息泵”,它允许用户在控制台中输入退出命令以优雅地关闭应用程序。在 MessagePump 中的循环之后,您可能希望以一种很好的方式清理线程使用的资源。出于以下几个原因,在那里比在 ProcessExit 中这样做更好:
- 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.
- 避免交叉线程问题;如果在 MessagePump 线程上创建了外部 COM 对象,则在那里处理它们会更容易。
- ProcessExit 有时间限制(默认情况下为 3 秒),因此如果清理很耗时,则在该事件处理程序中执行 pefrom 可能会失败。
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");
}
}
回答by JJ_Coder4Hire
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:
这是一个完整的、非常简单的 .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..");
}
}
}
回答by user79755
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.
该应用程序是一个服务器,它会一直运行到系统关闭或收到 Ctrl+C 或控制台窗口关闭为止。
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 (Actual OS) is actually shutting down.)
由于应用程序的特殊性,“优雅地”退出是不可行的。(可能是我可以编写另一个应用程序来发送“服务器关闭”消息,但这对于一个应用程序来说太过分了,并且在某些情况下仍然不够,例如服务器(实际操作系统)实际关闭时。)
Because of these circumstances I added a "ConsoleCtrlHandler" where I stop my threads and clean up my COM objects etc...
由于这些情况,我添加了一个“ 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
This setup seems to work out perfectly. Here is a linkto some C# code for the same thing.
这个设置似乎很完美。这是同一件事的一些 C# 代码的链接。
回答by EricLaw
For the CTRL+C case, you can use this:
对于 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);
}
回答by Mike Dinescu
If you are using a console application and you are pumping messages, can't you use the WM_QUIT message?
如果您正在使用控制台应用程序并且正在发送消息,那么您不能使用 WM_QUIT 消息吗?
回答by stenly
As a good example may be worth it to navigate to this projectand see how to handle exiting processes grammatically or in this snippet from VM found in here
作为一个很好的例子,导航到这个项目并查看如何从语法上或在此处找到的 VM片段中查看如何处理退出进程可能是值得的
ConsoleOutputStream = new ObservableCollection<string>();
var startInfo = new ProcessStartInfo(FilePath)
{
WorkingDirectory = RootFolderPath,
Arguments = StartingArguments,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
};
ConsoleProcess = new Process {StartInfo = startInfo};
ConsoleProcess.EnableRaisingEvents = true;
ConsoleProcess.OutputDataReceived += (sender, args) =>
{
App.Current.Dispatcher.Invoke((System.Action) delegate
{
ConsoleOutputStream.Insert(0, args.Data);
//ConsoleOutputStream.Add(args.Data);
});
};
ConsoleProcess.Exited += (sender, args) =>
{
InProgress = false;
};
ConsoleProcess.Start();
ConsoleProcess.BeginOutputReadLine();
}
}
private void RegisterProcessWatcher()
{
startWatch = new ManagementEventWatcher(
new WqlEventQuery($"SELECT * FROM Win32_ProcessStartTrace where ProcessName = '{FileName}'"));
startWatch.EventArrived += new EventArrivedEventHandler(startProcessWatch_EventArrived);
stopWatch = new ManagementEventWatcher(
new WqlEventQuery($"SELECT * FROM Win32_ProcessStopTrace where ProcessName = '{FileName}'"));
stopWatch.EventArrived += new EventArrivedEventHandler(stopProcessWatch_EventArrived);
}
private void stopProcessWatch_EventArrived(object sender, EventArrivedEventArgs e)
{
InProgress = false;
}
private void startProcessWatch_EventArrived(object sender, EventArrivedEventArgs e)
{
InProgress = true;
}

