我有一个包含相当多线程的控制台应用程序。有线程监视某些条件并在程序正确时终止程序。这种终止可以随时发生。捕获控制台出口C#
我需要一个事件,可以在程序关闭时触发,以便我可以清理所有其他线程并正确关闭所有文件句柄和连接。我不确定是否有已经内置到.NET框架中的内容,所以我在写自己的代码之前要问。
我在想,如果有沿行的事件:
MyConsoleProgram.OnExit += CleanupBeforeExit;
我有一个包含相当多线程的控制台应用程序。有线程监视某些条件并在程序正确时终止程序。这种终止可以随时发生。捕获控制台出口C#
我需要一个事件,可以在程序关闭时触发,以便我可以清理所有其他线程并正确关闭所有文件句柄和连接。我不确定是否有已经内置到.NET框架中的内容,所以我在写自己的代码之前要问。
我在想,如果有沿行的事件:
MyConsoleProgram.OnExit += CleanupBeforeExit;
我不知道我在网上找到了哪些代码,但现在我在其中一个旧项目中找到了它。这将允许您在控制台中执行清理代码,例如当它被突然关闭或因关机...
[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)
{
switch (sig)
{
case CtrlType.CTRL_C_EVENT:
case CtrlType.CTRL_LOGOFF_EVENT:
case CtrlType.CTRL_SHUTDOWN_EVENT:
case CtrlType.CTRL_CLOSE_EVENT:
default:
return false;
}
}
static void Main(string[] args)
{
// Some biolerplate to react to close window event
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
...
}
更新
对于那些不检查看来,这个特殊的解决方案确实不工作做好的意见(或全部)在Windows 7。以下thread谈论这个
没有为WinForms的应用;
Application.ApplicationExit += CleanupBeforeExit;
对于控制台应用程序,尝试
AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;
但我不知道在什么时候被调用或是否会从当前域范围内工作。我怀疑不是。
DomainUnload的帮助文档说:“该事件的EventHandler委托可以在应用程序域之前执行任何终止活动被卸载。“所以它听起来像是在当前的域中起作用。但是,这可能无法满足他的需求,因为他的线程可能会使域名保持不变。 – 2009-03-23 16:58:01
这只能处理CTRL + C和CTRL + Close,它不会通过返回,Environment.Exit或单击关闭按钮来捕获。 – Kit10 2012-08-31 18:23:46
这听起来像你有线程直接终止应用程序?也许最好有一个线程信号通知主线程说应用程序应该被终止。
接收到此信号后,主线程可以干净地关闭其他线程并最终关闭它。
检查也:
AppDomain.CurrentDomain.ProcessExit
对于那些有兴趣在VB.net。 (我搜索了互联网,无法找到它的等价物)在这里它被翻译成vb.net。
<DllImport("kernel32")> _
Private Function SetConsoleCtrlHandler(ByVal HandlerRoutine As HandlerDelegate, ByVal Add As Boolean) As Boolean
End Function
Private _handler As HandlerDelegate
Private Delegate Function HandlerDelegate(ByVal dwControlType As ControlEventType) As Boolean
Private Function ControlHandler(ByVal controlEvent As ControlEventType) As Boolean
Select Case controlEvent
Case ControlEventType.CtrlCEvent, ControlEventType.CtrlCloseEvent
Console.WriteLine("Closing...")
Return True
Case ControlEventType.CtrlLogoffEvent, ControlEventType.CtrlBreakEvent, ControlEventType.CtrlShutdownEvent
Console.WriteLine("Shutdown Detected")
Return False
End Select
End Function
Sub Main()
Try
_handler = New HandlerDelegate(AddressOf ControlHandler)
SetConsoleCtrlHandler(_handler, True)
.....
End Sub
全部工作的例子,工程用CTRL-C,与X关闭车窗并杀死:
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..");
}
}
}
我也有过类似的问题,只是我的控制台应用程序,会在无限循环中运行中间有一个先发制人的声明。这里是我的解决方案:
class Program
{
static int Main(string[] args)
{
// Init Code...
Console.CancelKeyPress += Console_CancelKeyPress; // Register the function to cancel event
// I do my stuffs
while (true)
{
// Code ....
SomePreemptiveCall(); // The loop stucks here wating function to return
// Code ...
}
return 0; // Never comes here, but...
}
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
Console.WriteLine("Exiting");
// Termitate what I have to terminate
Environment.Exit(-1);
}
}
在评论由查理上述B提到的link到FLQ
内心深处说:如果你链接到USER32
SetConsoleCtrlHandler将无法在Windows7中工作
线程中的某些地方建议创建一个隐藏的窗口。所以我创建了一个winform,并在onload中附加到控制台并执行原始Main。 然后SetConsoleCtrlHandle正常工作(由FLQ的建议SetConsoleCtrlHandle叫)
public partial class App3DummyForm : Form
{
private readonly string[] _args;
public App3DummyForm(string[] args)
{
_args = args;
InitializeComponent();
}
private void App3DummyForm_Load(object sender, EventArgs e)
{
AllocConsole();
App3.Program.OriginalMain(_args);
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
}
的Visual Studio 2015年+的Windows 10
代码:
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace YourNamespace
{
class Program
{
// if you want to allow only one instance otherwise remove the next line
static Mutex mutex = new Mutex(false, "YOURGUID-YOURGUID-YOURGUID-YO");
static ManualResetEvent run = new ManualResetEvent(true);
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler exitHandler;
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 ExitHandler(CtrlType sig)
{
Console.WriteLine("Shutting down: " + sig.ToString());
run.Reset();
Thread.Sleep(2000);
return false; // If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used (from MSDN).
}
static void Main(string[] args)
{
// if you want to allow only one instance otherwise remove the next 4 lines
if (!mutex.WaitOne(TimeSpan.FromSeconds(2), false))
{
return; // singleton application already started
}
exitHandler += new EventHandler(ExitHandler);
SetConsoleCtrlHandler(exitHandler, true);
try
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.ForegroundColor = ConsoleColor.Black;
Console.Clear();
Console.SetBufferSize(Console.BufferWidth, 1024);
Console.Title = "Your Console Title - XYZ";
// start your threads here
Thread thread1 = new Thread(new ThreadStart(ThreadFunc1));
thread1.Start();
Thread thread2 = new Thread(new ThreadStart(ThreadFunc2));
thread2.IsBackground = true; // a background thread
thread2.Start();
while (run.WaitOne(0))
{
Thread.Sleep(100);
}
// do thread syncs here signal them the end so they can clean up or use the manual reset event in them or abort them
thread1.Abort();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("fail: ");
Console.ForegroundColor = ConsoleColor.Black;
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("Inner: " + ex.InnerException.Message);
}
}
finally
{
// do app cleanup here
// if you want to allow only one instance otherwise remove the next line
mutex.ReleaseMutex();
// remove this after testing
Console.Beep(5000, 100);
}
}
public static void ThreadFunc1()
{
Console.Write("> ");
while ((line = Console.ReadLine()) != null)
{
if (line == "command 1")
{
}
else if (line == "command 1")
{
}
else if (line == "?")
{
}
Console.Write("> ");
}
}
public static void ThreadFunc2()
{
while (run.WaitOne(0))
{
Thread.Sleep(100);
}
// do thread cleanup here
Console.Beep();
}
}
}
ZeroKelvin的答案适用于Windows 10 x64,.NET 4.6控制台应用程序。对于那些谁也不需要处理CtrlType枚举,这里是挂接到框架的关闭一个非常简单的方法:
class Program
{
private delegate bool ConsoleCtrlHandlerDelegate(int sig);
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerDelegate handler, bool add);
static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;
static void Main(string[] args)
{
_consoleCtrlHandler += s =>
{
//DoCustomShutdownStuff();
return false;
};
SetConsoleCtrlHandler(_consoleCtrlHandler, true);
}
}
返回从处理程序FALSE告诉我们没有“处理”的控制框架信号,并使用此进程的处理程序列表中的下一个处理函数。如果没有任何处理程序返回TRUE,则调用默认处理程序。
请注意,当用户执行注销或关机时,回调不由Windows调用,而是立即终止。
我知道这是一个非常晚的评论,但如果“关闭文件和连接”是你想做清理的唯一事情,你并不需要这么做。因为Windows在终止过程中已关闭与进程关联的所有句柄。 – 2010-03-23 08:39:43
^只有当这些资源属于正在终止的进程。这是绝对必要的,例如,如果您在后台自动化隐藏的COM应用程序(例如Word或Excel),并且您需要确保在您的应用程序退出之前将其杀死,等等。 – BrainSlugs83 2013-11-17 07:58:01
这有一个短寻找答案http://stackoverflow.com/questions/2555292/how-to-run-code-before-program-exit – barlop 2016-03-18 14:49:28