ฉันต้องการดักจับCTRL+ Cในแอปพลิเคชันคอนโซล C # เพื่อให้ฉันสามารถดำเนินการสะสางก่อนที่จะออก วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร?
ฉันต้องการดักจับCTRL+ Cในแอปพลิเคชันคอนโซล C # เพื่อให้ฉันสามารถดำเนินการสะสางก่อนที่จะออก วิธีที่ดีที่สุดในการทำเช่นนี้คืออะไร?
คำตอบ:
กระบวนการConsole.CancelKeyPressใช้สำหรับการนี้ นี่คือวิธีการใช้งาน:
public static void Main(string[] args)
{
Console.CancelKeyPress += delegate {
// call methods to clean up
};
while (true) {}
}
เมื่อผู้ใช้กด Ctrl + C รหัสในผู้รับมอบสิทธิ์จะรันและโปรแกรมจะออก วิธีนี้ช่วยให้คุณสามารถล้างข้อมูลได้โดยการเรียกวิธีการที่จำเป็น โปรดทราบว่าไม่มีรหัสหลังจากผู้รับมอบสิทธิ์จะดำเนินการ
มีสถานการณ์อื่น ๆ ที่สิ่งนี้จะไม่ลดลง ตัวอย่างเช่นหากโปรแกรมกำลังทำการคำนวณที่สำคัญซึ่งไม่สามารถหยุดได้ทันที ในกรณีนั้นกลยุทธ์ที่ถูกต้องอาจบอกโปรแกรมให้ออกหลังจากการคำนวณเสร็จสมบูรณ์ รหัสต่อไปนี้เป็นตัวอย่างของวิธีการนำสิ่งนี้ไปปฏิบัติ:
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");
}
}
ความแตกต่างระหว่างรหัสนี้และตัวอย่างแรกe.Cancel
คือการตั้งค่าเป็นจริงซึ่งหมายความว่าการดำเนินการต่อหลังจากผู้รับมอบสิทธิ์ หากเรียกใช้โปรแกรมจะรอให้ผู้ใช้กด Ctrl + C เมื่อสิ่งนั้นเกิดขึ้นkeepRunning
ค่าการเปลี่ยนแปลงของตัวแปรจะทำให้ลูป while ออก นี่เป็นวิธีที่จะทำให้โปรแกรมออกจากโปรแกรมได้อย่างสวยงาม
keepRunning
volatile
อาจจะต้องมีการทำเครื่องหมาย เธรดหลักอาจแคชในการลงทะเบียน CPU เป็นอย่างอื่นและจะไม่สังเกตเห็นการเปลี่ยนแปลงค่าเมื่อดำเนินการผู้รับมอบสิทธิ์
ManualResetEvent
bool
winpty dotnet run
) มิฉะนั้นผู้รับมอบสิทธิ์จะไม่ถูกเรียกใช้
ฉันต้องการที่จะเพิ่มคำตอบโจนัส ปั่นบนbool
จะทำให้เกิดการใช้งาน CPU 100% และเสียพวงของพลังงานทำมากไม่มีอะไรในขณะที่รอให้+CTRLC
ทางออกที่ดีกว่าคือการใช้ a ManualResetEvent
เพื่อ "รอ" จริง ๆ กับ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();
}
นี่คือตัวอย่างการทำงานที่สมบูรณ์ วางในโครงการคอนโซล 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..");
}
}
}
คำถามนี้คล้ายกับ:
นี่คือวิธีที่ฉันแก้ไขปัญหานี้และจัดการกับผู้ใช้ที่กดปุ่ม X และ Ctrl-C สังเกตการใช้ ManualResetEvents สิ่งเหล่านี้จะทำให้เธรดหลักเข้าสู่โหมดสลีปซึ่งทำให้ CPU ประมวลผลเธรดอื่นในขณะที่รอการออกหรือการล้างข้อมูล หมายเหตุ: มีความจำเป็นต้องตั้งค่า 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;
}
}
}
Console.TreatControlCAsInput = true;
ได้ทำงานให้ฉัน
ฉันสามารถทำการสะสางก่อนออกได้ อะไรคือวิธีที่ดีที่สุดในการทำ Thats นี้คือเป้าหมายที่แท้จริง: กับดักทางออกเพื่อสร้างสิ่งของของคุณเอง และคำตอบของนีกเธอร์ไม่ได้ทำให้ถูกต้อง เนื่องจาก Ctrl + C เป็นเพียงหนึ่งในหลายวิธีในการออกจากแอป
สิ่งที่จำเป็นสำหรับ 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;
}
...
CancelKeyPress
จะกล่าวถึงเพียงสั้น ๆ ในความคิดเห็น บทความที่ดีคือcodeneverwritten.com/2006/10/…