Bir C # konsol uygulamasında CTRL+ yakalayabilmek Cistiyorum, böylece çıkmadan önce bazı temizlik işlemleri yapabilirim. Bunu yapmanın en iyi yolu nedir?
Bir C # konsol uygulamasında CTRL+ yakalayabilmek Cistiyorum, böylece çıkmadan önce bazı temizlik işlemleri yapabilirim. Bunu yapmanın en iyi yolu nedir?
Yanıtlar:
Bunun için Console.CancelKeyPress olayı kullanılır. Bu şekilde kullanılır:
public static void Main(string[] args)
{
Console.CancelKeyPress += delegate {
// call methods to clean up
};
while (true) {}
}
Kullanıcı Ctrl + C tuşlarına bastığında, temsilci içindeki kod çalıştırılır ve programdan çıkar. Bu, necessairy yöntemlerini çağırarak temizleme gerçekleştirmenizi sağlar. Temsilci çalıştırıldıktan sonra hiçbir kod olmadığını unutmayın.
Bunun kesmeyeceği başka durumlar da var. Örneğin, program şu anda hemen durdurulamayan önemli hesaplamalar yapıyorsa. Bu durumda, doğru strateji, hesaplama tamamlandıktan sonra programdan çıkılmasını söylemek olabilir. Aşağıdaki kod, bunun nasıl uygulanabileceğine ilişkin bir örnek vermektedir:
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");
}
}
Bu kod ile ilk örnek arasındaki fark e.Cancel
, true olarak ayarlanmış olmasıdır; Çalıştırılırsa, program kullanıcının Ctrl + C tuşlarına basmasını bekler. Bu durumda keepRunning
değişken, while döngüsünün çıkmasına neden olan değeri değiştirir. Bu programın zarif bir şekilde çıkmasını sağlamanın bir yoludur.
keepRunning
işaretlenmesi gerekebilir volatile
. Ana iş parçacığı bunu bir CPU kaydında önbelleğe alabilir ve temsilci yürütüldüğünde değer değişikliğini fark etmez.
ManualResetEvent
yerine döndürmek yerine değiştirilmelidir bool
.
winpty dotnet run
). Aksi takdirde, temsilci asla çalıştırılmaz.
Jonas'ın cevabına eklemek istiyorum . Bir eğirme, bool
% 100 CPU kullanımına neden olur ve CTRL+ için beklerken bir sürü enerji harcamaz C.
Daha iyi bir çözüm, ManualResetEvent
aslında CTRL+ için "beklemek" için a kullanmaktır 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();
}
İşte tam bir çalışma örneği. boş C # konsol projesine yapıştırın:
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..");
}
}
}
Bu soru şuna çok benzer:
İşte bu sorunu nasıl çözdüm ve kullanıcı X yanı sıra Ctrl-C isabet ele. ManualResetEvents kullanmaya dikkat edin. Bunlar, ana iş parçacığının uykuya neden olmasına neden olur; Not: ana sonunda TerminationCompletedEvent ayarlamak gerekir. Bunun yapılmaması, uygulamayı öldürürken işletim sistemi zaman aşımına uğraması nedeniyle sonlandırmanın gereksiz gecikmesine neden olur.
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;
}
}
}
Çıkmadan önce bazı temizlik işlemleri yapabilirim. Bunu yapmanın en iyi yolu nedir bu gerçek hedef: tuzak çıkış, kendi şeyler yapmak. Ve yukarıda verilen diğer cevaplar doğru yapmıyor. Çünkü, Ctrl + C uygulamadan çıkmanın birçok yolundan sadece biridir.
Bunun için dotnet c # 'a ne gerek var - iptal belirteci geçti Host.RunAsync(ct)
ve sonra çıkış sinyalleri tuzaklarında, Windows için
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;
}
...
CancelKeyPress
ve yorumlarda sadece kısaca bahsedilmektedir. İyi bir makale codeneverwritten.com/2006/10/…