捕获控制台出口C#

我有一个包含相当多的线程的控制台应用程序。 有线程监视某些条件,如果它们是真的就终止程序。 这个终止可以在任何时候发生。

我需要一个事件,可以在程序closures时触发,以便我可以清理所有其他线程并正确closures所有文件句柄和连接。 我不确定是否有一个已经内置到.NET框架,所以我问我写我自己的。

我想知道是否有这样一个事件:

MyConsoleProgram.OnExit += CleanupBeforeExit; 

我不知道我在网上find了哪些代码,但是现在我在其中一个旧项目中find了它。 这将允许你在你的控制台做清理代码,例如,当它突然closures或由于关机…

 [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) { switch (sig) { case CtrlType.CTRL_C_EVENT: case CtrlType.CTRL_LOGOFF_EVENT: case CtrlType.CTRL_SHUTDOWN_EVENT: case CtrlType.CTRL_CLOSE_EVENT: default: return false; } } static void Main(string[] args) { // Some biolerplate to react to close window event _handler += new EventHandler(Handler); SetConsoleCtrlHandler(_handler, true); ... } 

更新

对于那些没有检查评论的人来说,这个特殊的解决scheme似乎并不能Windows 7上正常工作。 下面的线程谈到这一点

完整的工作示例,与ctrl-c一起工作,用Xclosures窗口并杀死:

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

还请检查:

 AppDomain.CurrentDomain.ProcessExit 

有WinForms应用程序;

 Application.ApplicationExit += CleanupBeforeExit; 

对于控制台应用,请尝试

 AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit; 

但是我不确定在什么时候被调用,或者是否能在当前的域中工作。 我怀疑不是。

这听起来像你有线程直接终止应用程序? 也许最好有一个线程信号的主线程,说应用程序应该被终止。

接收到这个信号后,主线程可以干净地closures其他线程,最后closures。

我有一个类似的问题,只是我的控制台应用程序将运行在一个无限循环与中间一个先发制人的声明。 这是我的解决scheme:

 class Program { static int Main(string[] args) { // Init Code... Console.CancelKeyPress += Console_CancelKeyPress; // Register the function to cancel event // I do my stuffs while ( true ) { // Code .... SomePreemptiveCall(); // The loop stucks here wating function to return // Code ... } return 0; // Never comes here, but... } static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e) { Console.WriteLine("Exiting"); // Termitate what I have to terminate Environment.Exit(-1); } } 

查尔B在上面提到的链接在flq评论

内心深处说:

如果链接到user32,SetConsoleCtrlHandler将无法在Windows7上工作

build议在线程中的其他位置创build一个隐藏的窗口。 所以我创build一个winform,并在onload我连接到控制台,并执行原始的主。 然后SetConsoleCtrlHandle工作正常(SetConsoleCtrlHandle被称为由flqbuild议)

 public partial class App3DummyForm : Form { private readonly string[] _args; public App3DummyForm(string[] args) { _args = args; InitializeComponent(); } private void App3DummyForm_Load(object sender, EventArgs e) { AllocConsole(); App3.Program.OriginalMain(_args); } [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] static extern bool AllocConsole(); } 

对于那些对VB.net感兴趣的人。 (我search了互联网,找不到它的等价物)在这里它被翻译成vb.net。

  <DllImport("kernel32")> _ Private Function SetConsoleCtrlHandler(ByVal HandlerRoutine As HandlerDelegate, ByVal Add As Boolean) As Boolean End Function Private _handler As HandlerDelegate Private Delegate Function HandlerDelegate(ByVal dwControlType As ControlEventType) As Boolean Private Function ControlHandler(ByVal controlEvent As ControlEventType) As Boolean Select Case controlEvent Case ControlEventType.CtrlCEvent, ControlEventType.CtrlCloseEvent Console.WriteLine("Closing...") Return True Case ControlEventType.CtrlLogoffEvent, ControlEventType.CtrlBreakEvent, ControlEventType.CtrlShutdownEvent Console.WriteLine("Shutdown Detected") Return False End Select End Function Sub Main() Try _handler = New HandlerDelegate(AddressOf ControlHandler) SetConsoleCtrlHandler(_handler, True) ..... End Sub 

Visual Studio 2015 + Windows 10

  • 允许清理
  • 单一实例应用
  • 一些镀金

码:

 using System; using System.Linq; using System.Runtime.InteropServices; using System.Threading; namespace YourNamespace { class Program { // if you want to allow only one instance otherwise remove the next line static Mutex mutex = new Mutex(false, "YOURGUID-YOURGUID-YOURGUID-YO"); static ManualResetEvent run = new ManualResetEvent(true); [DllImport("Kernel32")] private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add); private delegate bool EventHandler(CtrlType sig); static EventHandler exitHandler; 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 ExitHandler(CtrlType sig) { Console.WriteLine("Shutting down: " + sig.ToString()); run.Reset(); Thread.Sleep(2000); return false; // If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used (from MSDN). } static void Main(string[] args) { // if you want to allow only one instance otherwise remove the next 4 lines if (!mutex.WaitOne(TimeSpan.FromSeconds(2), false)) { return; // singleton application already started } exitHandler += new EventHandler(ExitHandler); SetConsoleCtrlHandler(exitHandler, true); try { Console.BackgroundColor = ConsoleColor.Gray; Console.ForegroundColor = ConsoleColor.Black; Console.Clear(); Console.SetBufferSize(Console.BufferWidth, 1024); Console.Title = "Your Console Title - XYZ"; // start your threads here Thread thread1 = new Thread(new ThreadStart(ThreadFunc1)); thread1.Start(); Thread thread2 = new Thread(new ThreadStart(ThreadFunc2)); thread2.IsBackground = true; // a background thread thread2.Start(); while (run.WaitOne(0)) { Thread.Sleep(100); } // do thread syncs here signal them the end so they can clean up or use the manual reset event in them or abort them thread1.Abort(); } catch (Exception ex) { Console.ForegroundColor = ConsoleColor.Red; Console.Write("fail: "); Console.ForegroundColor = ConsoleColor.Black; Console.WriteLine(ex.Message); if (ex.InnerException != null) { Console.WriteLine("Inner: " + ex.InnerException.Message); } } finally { // do app cleanup here // if you want to allow only one instance otherwise remove the next line mutex.ReleaseMutex(); // remove this after testing Console.Beep(5000, 100); } } public static void ThreadFunc1() { Console.Write("> "); while ((line = Console.ReadLine()) != null) { if (line == "command 1") { } else if (line == "command 1") { } else if (line == "?") { } Console.Write("> "); } } public static void ThreadFunc2() { while (run.WaitOne(0)) { Thread.Sleep(100); } // do thread cleanup here Console.Beep(); } } } 

ZeroKelvin的答案适用于Windows 10 x64,.NET 4.6控制台应用程序。 对于那些不需要处理CtrlType枚举的人来说,这是一个非常简单的方法来挂钩框架的closures:

 class Program { private delegate bool ConsoleCtrlHandlerDelegate(int sig); [DllImport("Kernel32")] private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerDelegate handler, bool add); static ConsoleCtrlHandlerDelegate _consoleCtrlHandler; static void Main(string[] args) { _consoleCtrlHandler += s => { //DoCustomShutdownStuff(); return false; }; SetConsoleCtrlHandler(_consoleCtrlHandler, true); } } 

从处理程序中返回FALSE告诉框架,我们没有“处理”控制信号,并且使用该处理的处理程序列表中的下一个处理函数。 如果没有处理程序返回TRUE,则调用默认处理程序。

请注意,当用户执行注销或closures时,Windows不会调用callback,而是立即终止callback。