如何在 C# 控制台应用程序中捕获 ctrl-c (SIGINT)
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/177856/
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
How do I trap ctrl-c (SIGINT) in a C# console app
提问by Nick Randell
I would like to be able to trap CTRL+Cin a C# console application so that I can carry out some cleanups before exiting. What is the best way of doing this?
我希望能够在 C# 控制台应用程序中捕获CTRL+ C,以便我可以在退出之前进行一些清理。这样做的最佳方法是什么?
采纳答案by aku
回答by Jonas
The Console.CancelKeyPressevent is used for this. This is how it's used:
该Console.CancelKeyPress事件被用于此目的。这是它的使用方式:
public static void Main(string[] args)
{
Console.CancelKeyPress += delegate {
// call methods to clean up
};
while (true) {}
}
When the user presses Ctrl + C the code in the delegate is run and the program exits. This allows you to perform cleanup by calling necessairy methods. Note that no code after the delegate is executed.
当用户按下 Ctrl + C 时,委托中的代码运行并退出程序。这允许您通过调用必要的方法来执行清理。请注意,执行委托后没有代码。
There are other situations where this won't cut it. For example, if the program is currently performing important calculations that can't be immediately stopped. In that case, the correct strategy might be to tell the program to exit after the calculation is complete. The following code gives an example of how this can be implemented:
在其他情况下,这不会削减它。例如,如果程序当前正在执行无法立即停止的重要计算。在这种情况下,正确的策略可能是在计算完成后告诉程序退出。以下代码给出了如何实现的示例:
class MainClass
{
private static bool keepRunning = true;
public static void Main(string[] args)
{
Console.CancelKeyPress += delegate(object sender, ConsoleCancelEventArgs e) {
e.Cancel = true;
MainClass.keepRunning = false;
};
while (MainClass.keepRunning) {
// Do your work in here, in small chunks.
// If you literally just want to wait until ctrl-c,
// not doing anything, see the answer using set-reset events.
}
Console.WriteLine("exited gracefully");
}
}
The difference between this code and the first example is that e.Cancel
is set to true, which means the execution continues after the delegate. If run, the program waits for the user to press Ctrl + C. When that happens the keepRunning
variable changes value which causes the while loop to exit. This is a way to make the program exit gracefully.
此代码与第一个示例之间的区别在于e.Cancel
设置为 true,这意味着在委托之后继续执行。如果运行,程序会等待用户按 Ctrl + C。当发生这种情况时,keepRunning
变量会更改值,从而导致 while 循环退出。这是使程序优雅退出的一种方式。
回答by Jonathon Reinhart
I'd like to add to Jonas' answer. Spinning on a bool
will cause 100% CPU utilization, and waste a bunch of energy doing a lot of nothing while waiting for CTRL+C.
我想补充乔纳斯的回答。旋转 abool
将导致 100% 的 CPU 利用率,并在等待CTRL+ 时浪费大量精力无所事事C。
The better solution is to use a ManualResetEvent
to actually "wait" for the CTRL+C:
更好的解决方案是使用 aManualResetEvent
来实际“等待” CTRL+ C:
static void Main(string[] args) {
var exitEvent = new ManualResetEvent(false);
Console.CancelKeyPress += (sender, eventArgs) => {
eventArgs.Cancel = true;
exitEvent.Set();
};
var server = new MyServer(); // example
server.Run();
exitEvent.WaitOne();
server.Stop();
}
回答by JJ_Coder4Hire
Here is a complete working example. paste into empty C# console project:
这是一个完整的工作示例。粘贴到空的 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 hallo
Console.TreatControlCAsInput = true;
has worked for me.
Console.TreatControlCAsInput = true;
对我有用。
回答by Paul
This question is very similar to:
这个问题非常类似于:
Here is how I solved this problem, and dealt with the user hitting the X as well as Ctrl-C. Notice the use of ManualResetEvents. These will cause the main thread to sleep which frees the CPU to process other threads while waiting for either exit, or cleanup. NOTE: It is necessary to set the TerminationCompletedEvent at the end of main. Failure to do so causes unnecessary latency in termination due to the OS timing out while killing the application.
这是我如何解决这个问题,并处理用户点击 X 以及 Ctrl-C。注意 ManualResetEvents 的使用。这些将导致主线程休眠,从而释放 CPU 以在等待退出或清理时处理其他线程。注意:有必要在 main 的末尾设置 TerminationCompletedEvent。由于操作系统在终止应用程序时超时,否则会导致不必要的终止延迟。
namespace CancelSample
{
using System;
using System.Threading;
using System.Runtime.InteropServices;
internal class Program
{
/// <summary>
/// Adds or removes an application-defined HandlerRoutine function from the list of handler functions for the calling process
/// </summary>
/// <param name="handler">A pointer to the application-defined HandlerRoutine function to be added or removed. This parameter can be NULL.</param>
/// <param name="add">If this parameter is TRUE, the handler is added; if it is FALSE, the handler is removed.</param>
/// <returns>If the function succeeds, the return value is true.</returns>
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(ConsoleCloseHandler handler, bool add);
/// <summary>
/// The console close handler delegate.
/// </summary>
/// <param name="closeReason">
/// The close reason.
/// </param>
/// <returns>
/// True if cleanup is complete, false to run other registered close handlers.
/// </returns>
private delegate bool ConsoleCloseHandler(int closeReason);
/// <summary>
/// Event set when the process is terminated.
/// </summary>
private static readonly ManualResetEvent TerminationRequestedEvent;
/// <summary>
/// Event set when the process terminates.
/// </summary>
private static readonly ManualResetEvent TerminationCompletedEvent;
/// <summary>
/// Static constructor
/// </summary>
static Program()
{
// Do this initialization here to avoid polluting Main() with it
// also this is a great place to initialize multiple static
// variables.
TerminationRequestedEvent = new ManualResetEvent(false);
TerminationCompletedEvent = new ManualResetEvent(false);
SetConsoleCtrlHandler(OnConsoleCloseEvent, true);
}
/// <summary>
/// The main console entry point.
/// </summary>
/// <param name="args">The commandline arguments.</param>
private static void Main(string[] args)
{
// Wait for the termination event
while (!TerminationRequestedEvent.WaitOne(0))
{
// Something to do while waiting
Console.WriteLine("Work");
}
// Sleep until termination
TerminationRequestedEvent.WaitOne();
// Print a message which represents the operation
Console.WriteLine("Cleanup");
// Set this to terminate immediately (if not set, the OS will
// eventually kill the process)
TerminationCompletedEvent.Set();
}
/// <summary>
/// Method called when the user presses Ctrl-C
/// </summary>
/// <param name="reason">The close reason</param>
private static bool OnConsoleCloseEvent(int reason)
{
// Signal termination
TerminationRequestedEvent.Set();
// Wait for cleanup
TerminationCompletedEvent.WaitOne();
// Don't run other handlers, just exit.
return true;
}
}
}
回答by Vaulter
I can carry out some cleanups before exiting. What is the best way of doing this Thats is the real goal: trap exit, to make your own stuff. And neigther answers above not makeing it right. Because, Ctrl+C is just one of many ways to exiting app.
我可以在退出前进行一些清理。这样做的最佳方式是什么 这才是真正的目标:陷阱出口,制作自己的东西。上面没有正确的答案。因为,Ctrl+C 只是退出应用程序的众多方法之一。
What in dotnet c# is needed for it - so called cancellation tokenpassed to Host.RunAsync(ct)
and then, in exit signals traps, for Windows it would be
它需要 dotnet c# 中的什么 - 所谓的取消令牌传递给Host.RunAsync(ct)
然后,在退出信号陷阱中,对于 Windows,它将是
private static readonly CancellationTokenSource cts = new CancellationTokenSource();
public static int Main(string[] args)
{
// For gracefull shutdown, trap unload event
AppDomain.CurrentDomain.ProcessExit += (sender, e) =>
{
cts.Cancel();
exitEvent.Wait();
};
Console.CancelKeyPress += (sender, e) =>
{
cts.Cancel();
exitEvent.Wait();
};
host.RunAsync(cts);
Console.WriteLine("Shutting down");
exitEvent.Set();
return 0;
}
...
...