W rzeczywistości ten artykuł zaleca P / Invoke i CancelKeyPressjest wspomniany tylko krótko w komentarzach. Dobry artykuł to codeneverwritten.com/2006/10/10/…
bzlm
Działa to, ale nie uwięzienie zamknięcia okna za pomocą X. Zobacz moje kompletne rozwiązanie poniżej. działa również z kill
JJ_Coder4Hire
1
Przekonałem się, że Console.CancelKeyPress przestanie działać, jeśli konsola zostanie zamknięta. Uruchamianie aplikacji pod mono / linux z systememd lub jeśli aplikacja działa jako „mono myapp.exe </ dev / null”, SIGINT zostanie wysłany do domyślnej procedury obsługi sygnału i natychmiast zabije aplikację. Użytkownicy Linuksa mogą chcieć zobaczyć stackoverflow.com/questions/6546509/...
@aku, czy nie ma czasu, aby odpowiedzieć na SIGINT, zanim proces zostanie przerwany? Nigdzie nie mogę go znaleźć i próbuję sobie przypomnieć, gdzie go przeczytałem.
publicstaticvoidMain(string[] args){Console.CancelKeyPress+=delegate{// call methods to clean up};while(true){}}
Gdy użytkownik naciśnie Ctrl + C, kod w delegacie jest uruchamiany i program kończy działanie. Pozwala to na wykonanie czyszczenia przez wywołanie niezbędnych metod. Pamiętaj, że po wykonaniu zadania nie ma kodu.
Są inne sytuacje, w których to nie da rady. Na przykład, jeśli program wykonuje obecnie ważne obliczenia, których nie można natychmiast zatrzymać. W takim przypadku poprawną strategią może być nakazanie programowi zakończenia pracy po zakończeniu obliczeń. Poniższy kod podaje przykład tego, jak można to zaimplementować:
classMainClass{privatestaticbool keepRunning =true;publicstaticvoidMain(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");}}
Różnica między tym kodem a pierwszym przykładem jest e.Cancelustawiona na true, co oznacza, że wykonywanie jest kontynuowane po delegowaniu. Jeśli zostanie uruchomiony, program czeka, aż użytkownik naciśnie Ctrl + C. W takim przypadku keepRunningzmienna zmienia wartość, co powoduje wyjście pętli while. Jest to sposób na wdzięczne zakończenie programu.
keepRunningmoże wymagać oznaczenia volatile. W przeciwnym razie główny wątek może buforować go w rejestrze procesora i nie zauważy zmiany wartości, gdy delegat zostanie wykonany.
cdhowie
Działa to, ale nie uwięzienie zamknięcia okna za pomocą X. Zobacz moje kompletne rozwiązanie poniżej. działa również z zabijaniem.
JJ_Coder4 Zatrudnij
13
Powinien zostać zmieniony, aby użyć ManualResetEventzamiast obracać na bool.
Mizipzor
Małe zastrzeżenie dla każdego, kto uruchamia rzeczy w Git-Bash, MSYS2 lub CygWin: Aby to zadziałało, musisz uruchomić dotnet przez winpty (Tak, winpty dotnet run). W przeciwnym razie delegat nigdy nie zostanie uruchomiony.
Chciałbym dodać do odpowiedzi Jonasa . Odwrócenie boolpowoduje 100% wykorzystanie procesora i marnuje mnóstwo energii, nie robiąc nic, czekając na CTRL+ C.
Lepszym rozwiązaniem jest użycie opcji ManualResetEvent„poczekaj” na CTRL+ C:
staticvoidMain(string[] args){var exitEvent =newManualResetEvent(false);Console.CancelKeyPress+=(sender, eventArgs)=>{
eventArgs.Cancel=true;
exitEvent.Set();};var server =newMyServer();// example
server.Run();
exitEvent.WaitOne();
server.Stop();}
Myślę, że chodzi o to, że wykonasz całą pracę w pętli while, a naciśnięcie Ctrl + C nie spowoduje przerwania w trakcie iteracji podczas; wykona tę iterację przed wybuchem.
pkr298
2
@ pkr298 - Szkoda, że ludzie nie głosują na twój komentarz, ponieważ jest to całkowicie prawda. Przeredaguję jego odpowiedź, aby wyjaśnić ludziom sposób myślenia Jonathona (co nie jest z natury złe, ale nie tak, jak Jonas miał na myśli swoją odpowiedź)
M. Mimpen,
Zaktualizuj istniejącą odpowiedź dzięki temu ulepszeniu.
Mizipzor
27
Oto kompletny przykład działania. wklej do pustego projektu konsoli C #:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace TestTrapCtrlC{publicclassProgram{staticbool exitSystem =false;#region Trap application termination[DllImport("Kernel32")]privatestaticexternboolSetConsoleCtrlHandler(EventHandler handler,booladd);privatedelegateboolEventHandler(CtrlType sig);staticEventHandler _handler;enumCtrlType{
CTRL_C_EVENT =0,
CTRL_BREAK_EVENT =1,
CTRL_CLOSE_EVENT =2,
CTRL_LOGOFF_EVENT =5,
CTRL_SHUTDOWN_EVENT =6}privatestaticboolHandler(CtrlType sig){Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown");//do your cleanup hereThread.Sleep(5000);//simulate some cleanup delayConsole.WriteLine("Cleanup complete");//allow main to run off
exitSystem =true;//shutdown right away so there are no lingering threadsEnvironment.Exit(-1);returntrue;}#endregionstaticvoidMain(string[] args){// Some biolerplate to react to close window event, CTRL-C, kill, etc
_handler +=newEventHandler(Handler);SetConsoleCtrlHandler(_handler,true);//start your multi threaded program hereProgram p =newProgram();
p.Start();//hold the console so it doesn’t run off the endwhile(!exitSystem){Thread.Sleep(500);}}publicvoidStart(){// start a thread and start doing some processingConsole.WriteLine("Thread started, processing..");}}}
Właśnie dodałem to, ponieważ jest to jedyna odpowiedź, która dotyczy pełnej odpowiedzi. Ten jest dokładny i pozwala uruchomić program w harmonogramie zadań bez udziału użytkownika. Nadal masz szansę na oczyszczenie. Użyj NLOG w swoim projekcie i masz coś do zarządzania. Zastanawiam się, czy skompiluje się w .NET Core 2 czy 3.
Oto jak rozwiązałem ten problem i poradziłem sobie z naciśnięciem klawisza X, a także Ctrl-C. Zwróć uwagę na użycie ManualResetEvents. Powodują one uśpienie głównego wątku, co pozwala procesorowi przetwarzać inne wątki podczas oczekiwania na wyjście lub czyszczenie. UWAGA: Konieczne jest ustawienie TerminationCompletedEvent na końcu Main. Niezastosowanie się do tego powoduje niepotrzebne opóźnienie w zakończeniu z powodu przekroczenia limitu czasu systemu operacyjnego podczas zabijania aplikacji.
namespace CancelSample{
using System;
using System.Threading;
using System.Runtime.InteropServices;internalclassProgram{/// <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")]privatestaticexternboolSetConsoleCtrlHandler(ConsoleCloseHandler handler,booladd);/// <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>privatedelegateboolConsoleCloseHandler(int closeReason);/// <summary>/// Event set when the process is terminated./// </summary>privatestaticreadonlyManualResetEventTerminationRequestedEvent;/// <summary>/// Event set when the process terminates./// </summary>privatestaticreadonlyManualResetEventTerminationCompletedEvent;/// <summary>/// Static constructor/// </summary>staticProgram(){// Do this initialization here to avoid polluting Main() with it// also this is a great place to initialize multiple static// variables.TerminationRequestedEvent=newManualResetEvent(false);TerminationCompletedEvent=newManualResetEvent(false);SetConsoleCtrlHandler(OnConsoleCloseEvent,true);}/// <summary>/// The main console entry point./// </summary>/// <param name="args">The commandline arguments.</param>privatestaticvoidMain(string[] args){// Wait for the termination eventwhile(!TerminationRequestedEvent.WaitOne(0)){// Something to do while waitingConsole.WriteLine("Work");}// Sleep until terminationTerminationRequestedEvent.WaitOne();// Print a message which represents the operationConsole.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>privatestaticboolOnConsoleCloseEvent(int reason){// Signal terminationTerminationRequestedEvent.Set();// Wait for cleanupTerminationCompletedEvent.WaitOne();// Don't run other handlers, just exit.returntrue;}}}
Może to spowodować, że ReadLine będzie wymagało dwóch naciśnięć Enter dla każdego wejścia.
Grault
0
Mogę przeprowadzić porządki przed wyjściem. Jaki jest najlepszy sposób na zrobienie tego. To jest prawdziwy cel: zamknij wyjście, aby tworzyć własne rzeczy. I powyższe odpowiedzi nie są poprawne. Ponieważ Ctrl + C to tylko jeden z wielu sposobów na wyjście z aplikacji.
Co jest potrzebne w dotnet c # - tak zwany token anulowania przekazywany do, Host.RunAsync(ct)a następnie, w pułapkach sygnałów wyjścia, dla Windows byłby
CancelKeyPress
jest wspomniany tylko krótko w komentarzach. Dobry artykuł to codeneverwritten.com/2006/10/10/…Do tego celu służy zdarzenie Console.CancelKeyPress . Oto jak jest używany:
Gdy użytkownik naciśnie Ctrl + C, kod w delegacie jest uruchamiany i program kończy działanie. Pozwala to na wykonanie czyszczenia przez wywołanie niezbędnych metod. Pamiętaj, że po wykonaniu zadania nie ma kodu.
Są inne sytuacje, w których to nie da rady. Na przykład, jeśli program wykonuje obecnie ważne obliczenia, których nie można natychmiast zatrzymać. W takim przypadku poprawną strategią może być nakazanie programowi zakończenia pracy po zakończeniu obliczeń. Poniższy kod podaje przykład tego, jak można to zaimplementować:
Różnica między tym kodem a pierwszym przykładem jest
e.Cancel
ustawiona na true, co oznacza, że wykonywanie jest kontynuowane po delegowaniu. Jeśli zostanie uruchomiony, program czeka, aż użytkownik naciśnie Ctrl + C. W takim przypadkukeepRunning
zmienna zmienia wartość, co powoduje wyjście pętli while. Jest to sposób na wdzięczne zakończenie programu.źródło
keepRunning
może wymagać oznaczeniavolatile
. W przeciwnym razie główny wątek może buforować go w rejestrze procesora i nie zauważy zmiany wartości, gdy delegat zostanie wykonany.ManualResetEvent
zamiast obracać nabool
.winpty dotnet run
). W przeciwnym razie delegat nigdy nie zostanie uruchomiony.Chciałbym dodać do odpowiedzi Jonasa . Odwrócenie
bool
powoduje 100% wykorzystanie procesora i marnuje mnóstwo energii, nie robiąc nic, czekając na CTRL+ C.Lepszym rozwiązaniem jest użycie opcji
ManualResetEvent
„poczekaj” na CTRL+ C:źródło
Oto kompletny przykład działania. wklej do pustego projektu konsoli C #:
źródło
To pytanie jest bardzo podobne do:
Przechwyć wyjście konsoli C #
Oto jak rozwiązałem ten problem i poradziłem sobie z naciśnięciem klawisza X, a także Ctrl-C. Zwróć uwagę na użycie ManualResetEvents. Powodują one uśpienie głównego wątku, co pozwala procesorowi przetwarzać inne wątki podczas oczekiwania na wyjście lub czyszczenie. UWAGA: Konieczne jest ustawienie TerminationCompletedEvent na końcu Main. Niezastosowanie się do tego powoduje niepotrzebne opóźnienie w zakończeniu z powodu przekroczenia limitu czasu systemu operacyjnego podczas zabijania aplikacji.
źródło
Console.TreatControlCAsInput = true;
pracował dla mnie.źródło
Co jest potrzebne w dotnet c # - tak zwany token anulowania przekazywany do,
Host.RunAsync(ct)
a następnie, w pułapkach sygnałów wyjścia, dla Windows byłby...
źródło