Tôi muốn có thể bẫy CTRL+ Ctrong ứng dụng bảng điều khiển C # để tôi có thể thực hiện một số dọn dẹp trước khi thoát. Cách nào là tốt nhất để thực hiện việc này?
Tôi muốn có thể bẫy CTRL+ Ctrong ứng dụng bảng điều khiển C # để tôi có thể thực hiện một số dọn dẹp trước khi thoát. Cách nào là tốt nhất để thực hiện việc này?
Câu trả lời:
Sự kiện Console.CatteryKeyPress được sử dụng cho việc này. Đây là cách nó được sử dụng:
public static void Main(string[] args)
{
Console.CancelKeyPress += delegate {
// call methods to clean up
};
while (true) {}
}
Khi người dùng nhấn Ctrl + C, mã trong ủy nhiệm được chạy và chương trình thoát. Điều này cho phép bạn thực hiện dọn dẹp bằng cách gọi các phương thức cần thiết. Lưu ý rằng không có mã nào sau khi ủy nhiệm được thực thi.
Có những tình huống khác mà điều này sẽ không cắt nó. Ví dụ: nếu chương trình hiện đang thực hiện các tính toán quan trọng không thể dừng ngay lập tức. Trong trường hợp đó, chiến lược chính xác có thể là yêu cầu chương trình thoát ra sau khi tính toán hoàn tất. Đoạn mã sau đây đưa ra một ví dụ về cách thực hiện điều này:
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");
}
}
Sự khác biệt giữa mã này và ví dụ đầu tiên là e.Cancel
được đặt thành true, có nghĩa là việc thực thi tiếp tục sau khi ủy nhiệm. Nếu chạy, chương trình sẽ đợi người dùng nhấn Ctrl + C. Khi điều đó xảy ra, keepRunning
biến số thay đổi giá trị khiến vòng lặp while thoát ra. Đây là một cách để làm cho chương trình thoát một cách duyên dáng.
keepRunning
có thể cần được đánh dấu volatile
. Chủ đề chính có thể lưu trữ nó trên một thanh ghi CPU nếu không sẽ không nhận thấy sự thay đổi giá trị khi ủy nhiệm được thực thi.
ManualResetEvent
thay vì quay trên a bool
.
winpty dotnet run
). Nếu không, đại biểu sẽ không bao giờ được điều hành.
Tôi muốn thêm vào câu trả lời của Jonas . Việc quay vòng trên bool
sẽ gây ra việc sử dụng CPU 100% và lãng phí rất nhiều năng lượng mà không làm được gì nhiều trong khi chờ đợi CTRL+ C.
Giải pháp tốt hơn là sử dụng một ManualResetEvent
để thực sự "chờ" cho 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();
}
Dưới đây là một ví dụ làm việc hoàn chỉnh. dán vào dự án giao diện điều khiển C # trống:
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..");
}
}
}
Câu hỏi này rất giống với:
Đây là cách tôi giải quyết vấn đề này và xử lý người dùng nhấn X cũng như Ctrl-C. Lưu ý việc sử dụng ManualResetEvents. Những điều này sẽ khiến luồng chính ngủ, giải phóng CPU để xử lý các luồng khác trong khi chờ thoát hoặc dọn dẹp. LƯU Ý: Cần đặt TerminationCompletedEvent ở cuối chính. Không làm như vậy gây ra độ trễ không cần thiết trong việc chấm dứt do hệ điều hành hết thời gian trong khi giết ứng dụng.
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;
}
}
}
Tôi có thể thực hiện một số dọn dẹp trước khi thoát. Cách tốt nhất để làm điều này Thats là mục tiêu thực sự: thoát bẫy, để tạo ra công cụ của riêng bạn. Và neigther câu trả lời ở trên không làm cho nó đúng. Bởi vì, Ctrl + C chỉ là một trong nhiều cách để thoát ứng dụng.
Cái gì trong dotnet c # là cần thiết cho nó - cái gọi là mã thông báo hủy được chuyển đến Host.RunAsync(ct)
và sau đó, trong bẫy tín hiệu thoát, đối với Windows, nó sẽ là
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
chỉ được đề cập ngắn gọn trong các ý kiến. Một bài viết hay là codeneverwritten.com / 2006/10 / b