捕获控制台退出 C#
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/474679/
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
Capture console exit C#
提问by ZeroKelvin
I have a console application that contains quite a lot of threads. There are threads that monitor certain conditions and terminate the program if they are true. This termination can happen at any time.
我有一个包含很多线程的控制台应用程序。有些线程会监视某些条件并在它们为真时终止程序。这种终止可能随时发生。
I need an event that can be triggered when the program is closing so that I can cleanup all of the other threads and close all file handles and connections properly. I'm not sure if there is one already built into the .NET framework, so I'm asking before I write my own.
我需要一个可以在程序关闭时触发的事件,以便我可以清理所有其他线程并正确关闭所有文件句柄和连接。我不确定 .NET 框架中是否已经内置了一个,所以我在编写自己的框架之前先询问一下。
I was wondering if there was an event along the lines of:
我想知道是否有这样的事件:
MyConsoleProgram.OnExit += CleanupBeforeExit;
采纳答案by flq
I am not sure where I found the code on the web, but I found it now in one of my old projects. This will allow you to do cleanup code in your console, e.g. when it is abruptly closed or due to a shutdown...
我不确定我在网络上的何处找到了代码,但我现在在我的一个旧项目中找到了它。这将允许您在控制台中执行清理代码,例如当它突然关闭或由于关机...
[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);
...
}
Update
更新
For those not checking the comments it seems that this particular solution does notwork well (or at all) on Windows 7. The following threadtalks about this
对于那些不检查评论的人来说,这个特定的解决方案似乎在Windows 7上不能很好地(或根本不能)运行。以下线程讨论了这一点
回答by Rob Prouse
There is for WinForms apps;
有用于 WinForms 应用程序;
Application.ApplicationExit += CleanupBeforeExit;
For Console apps, try
对于控制台应用程序,请尝试
AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;
But I am not sure at what point that gets called or if it will work from within the current domain. I suspect not.
但我不确定在什么时候被调用,或者它是否可以在当前域内工作。我怀疑不是。
回答by Rob
It sounds like you have the threads directly terminating the application? Perhaps it would be better to have a thread signal the main thread to say that the application should be terminated.
听起来您有线程直接终止应用程序?也许最好让一个线程向主线程发出信号,告知应用程序应该终止。
On receiving this signal, the main thread can cleanly shutdown the other threads and finally close itself down.
收到此信号后,主线程可以干净地关闭其他线程并最终关闭自己。
回答by jmservera
Check also:
还要检查:
AppDomain.CurrentDomain.ProcessExit
回答by dko
For those interested in VB.net. (I searched the internet and couldn't find an equivalent for it) Here it is translated into vb.net.
对于那些对 VB.net 感兴趣的人。(我在互联网上搜索并找不到它的等效项)这里是翻译成 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
回答by JJ_Coder4Hire
Full working example, works with ctrl-c, closing the windows with X and kill:
完整的工作示例,使用 ctrl-c,使用 X 关闭窗口并杀死:
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 boilerplate 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 Jo?o Portela
I've had a similar problem, just my console App would be running in infinite loop with one preemptive statement on middle. Here is my solution:
我遇到了类似的问题,只是我的控制台应用程序将在无限循环中运行,中间有一个抢占式语句。这是我的解决方案:
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);
}
}
回答by Jens
The linkmentioned above by Charle B in comment to flq
所述链接到FLQ评论上述由查理乙
Deep down says:
内心深处 说道:
SetConsoleCtrlHandler won't work on windows7 if you link to user32
如果您链接到 user32,SetConsoleCtrlHandler 将无法在 windows7 上工作
Some where else in the thread it is suggested to crate a hidden window. So I create a winform and in onload I attached to console and execute original Main. And then SetConsoleCtrlHandle works fine (SetConsoleCtrlHandle is called as suggested by flq)
建议在线程中的其他一些地方创建一个隐藏的窗口。因此,我创建了一个 winform 并在 onload 中附加到控制台并执行原始 Main。然后 SetConsoleCtrlHandle 工作正常(按照 flq 的建议调用 SetConsoleCtrlHandle)
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();
}
回答by A.J.Bauer
Visual Studio 2015 + Windows 10
Visual Studio 2015 + Windows 10
- Allow for cleanup
- Single instance app
- Some goldplating
- 允许清理
- 单实例应用
- 一些镀金
Code:
代码:
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();
}
}
}
回答by BCA
ZeroKelvin's answer works in Windows 10 x64, .NET 4.6 console app. For those who do not need to deal with the CtrlType enum, here is a really simple way to hook into the framework's shutdown:
ZeroKelvin 的答案适用于 Windows 10 x64、.NET 4.6 控制台应用程序。对于那些不需要处理 CtrlType 枚举的人,这里有一个非常简单的方法来挂钩框架的关闭:
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);
}
}
Returning FALSE from the handler tells the framework that we are not "handling" the control signal, and the next handler function in the list of handlers for this process is used. If none of the handlers returns TRUE, the default handler is called.
从处理程序返回 FALSE 告诉框架我们没有“处理”控制信号,并且使用此进程的处理程序列表中的下一个处理程序函数。如果没有处理程序返回 TRUE,则调用默认处理程序。
Note that when the user performs a logoff or shutdown, the callback is not called by Windows but is instead terminated immediately.
请注意,当用户执行注销或关机时,Windows 不会调用回调,而是立即终止。