在 .NET 中,是否有一种方法(例如事件)来检测控制台应用程序何时退出?你可能需要清理一些线程和 COM 对象.,记录一下信息等等。。。。。。。
比如我的数据采集软件,每次启动和退出时向钉钉推送相关信息。
代码语言:javascript复制 private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
{
//钉钉消息推送
DingDing.SendText(Environment.MachineName " 手动关闭 EdgeServices", new List<string> { });
// do some work 比如,本地消息记录等。。。。
踩坑过程:
尝试向 Process.GetCurrentProcess.Exited 和 Process.GetCurrentProcess.Disposed 添加处理程序.
也尝试向 Application.ApplicationExit 和 Application.ThreadExit 事件添加处理程序,但它们没有触发.
推荐
使用 ProcessExit 事件 AppDomain:
代码语言:javascript复制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 秒),因此如果清理很耗时,则在该事件处理程序中执行 pefrom 可能会失败.
代码如下:
代码语言:javascript复制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");
}
}
其他推荐答案
这是一个完整的、非常简单的 .Net 解决方案,适用于所有版本的 windows.只需将它粘贴到一个新项目中,运行它并尝试使用 CTRL-C 来查看它是如何处理它的:
代码语言:javascript复制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..");
}
}
}
其他推荐答案
应用程序是一个服务器,它会一直运行到系统关闭或收到 Ctrl C 或控制台窗口关闭为止.
由于应用程序的特殊性,"优雅地"退出是不可行的.(可能我可以编写另一个应用程序来发送"服务器关闭"消息,但这对于一个应用程序来说太过分了,并且在某些情况下仍然不够,例如服务器(实际操作系统)实际关闭时.)
由于这些情况,我添加了一个"ConsoleCtrlHandler" 在那里我停止我的线程并清理我的 COM 对象等...
代码语言:javascript复制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
这个设置似乎很完美。
一般情况下使用第一种最简单的方式就可以了。