我想为我的控制台应用程序做一个安全的退出,该应用程序将在Linux上使用mono进行运行,但我无法找到解决方案来检测是否发送了一个信号,或者用户按下了ctrl + c。控制台应用程序关闭/终止时检测?
在windows上有内核函数SetConsoleCtrlHandler,它可以完成这项工作,但是它不适用于单声道。
如何在我的控制台应用程序上获得关闭事件以安全退出?
我想为我的控制台应用程序做一个安全的退出,该应用程序将在Linux上使用mono进行运行,但我无法找到解决方案来检测是否发送了一个信号,或者用户按下了ctrl + c。控制台应用程序关闭/终止时检测?
在windows上有内核函数SetConsoleCtrlHandler,它可以完成这项工作,但是它不适用于单声道。
如何在我的控制台应用程序上获得关闭事件以安全退出?
您需要使用Mono.UnixSignal
,有张贴由乔纳森·普赖尔一个很好的例子:http://www.jprl.com/Blog/archive/development/mono/2008/Feb-08.html
还有单页上更短的例子:FAQ/Technical/Operating System Questions/Signal Handling:
// Catch SIGINT and SIGUSR1
UnixSignal[] signals = new UnixSignal [] {
new UnixSignal (Mono.Unix.Native.Signum.SIGINT),
new UnixSignal (Mono.Unix.Native.Signum.SIGUSR1),
};
Thread signal_thread = new Thread (delegate() {
while (true) {
// Wait for a signal to be delivered
int index = UnixSignal.WaitAny (signals, -1);
Mono.Unix.Native.Signum signal = signals [index].Signum;
// Notify the main thread that a signal was received,
// you can use things like:
// Application.Invoke() for Gtk#
// Control.Invoke on Windows.Forms
// Write to a pipe created with UnixPipes for server apps.
// Use an AutoResetEvent
// For example, this works with Gtk#
Application.Invoke (delegate() { ReceivedSignal (signal); });
}});
作为提供UNIX的例子,窗口的实现见下文。请注意,使用Visual Studio时,您仍可以包含Mono.Posix dll。
我已经添加了SIGTERM信号,因为在停止/重新启动您的应用程序作为服务时,这是由systemd在unix中触发的。
接口暴露退出事件
public interface IExitSignal
{
event EventHandler Exit;
}
UNIX实现
public class UnixExitSignal : IExitSignal
{
public event EventHandler Exit;
UnixSignal[] signals = new UnixSignal[]{
new UnixSignal(Mono.Unix.Native.Signum.SIGTERM),
new UnixSignal(Mono.Unix.Native.Signum.SIGINT),
new UnixSignal(Mono.Unix.Native.Signum.SIGUSR1)
};
public UnixExitSignal()
{
Task.Factory.StartNew(() =>
{
// blocking call to wait for any kill signal
int index = UnixSignal.WaitAny(signals, -1);
if (Exit != null)
{
Exit(null, EventArgs.Empty);
}
});
}
}
Windows实现
public class WinExitSignal : IExitSignal
{
public event EventHandler Exit;
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
// A delegate type to be used as the handler routine
// for SetConsoleCtrlHandler.
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
// An enumerated type for the control messages
// sent to the handler routine.
public enum CtrlTypes
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
/// <summary>
/// Need this as a member variable to avoid it being garbage collected.
/// </summary>
private HandlerRoutine m_hr;
public WinExitSignal()
{
m_hr = new HandlerRoutine(ConsoleCtrlCheck);
SetConsoleCtrlHandler(m_hr, true);
}
/// <summary>
/// Handle the ctrl types
/// </summary>
/// <param name="ctrlType"></param>
/// <returns></returns>
private bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
switch (ctrlType)
{
case CtrlTypes.CTRL_C_EVENT:
case CtrlTypes.CTRL_BREAK_EVENT:
case CtrlTypes.CTRL_CLOSE_EVENT:
case CtrlTypes.CTRL_LOGOFF_EVENT:
case CtrlTypes.CTRL_SHUTDOWN_EVENT:
if (Exit != null)
{
Exit(this, EventArgs.Empty);
}
break;
default:
break;
}
return true;
}
}
请注意,我有Task.Factory.StartNew的问题,似乎与专用的后台线程(当在码头集装箱中使用单声道)修复;有人遇到过这种情况么? –
以及如何调用看起来像一个控制台应用程序?你错过了}从 – Prix
我找不到在我的项目上引用的Mono.Posix,你可以给我一些额外的方向,在这个问题上...这两个链接是非常不完整的;/ – Prix
@Prix:Mono.Posix .dll是由标准的Mono 2.10.2安装程序安装的,您使用的是什么发行版?另外,对于控制台应用程序,您可以跳过'Application.Invoke'并从信号线程中调用清除逻辑。 – skolima