我的 .NET 5 控制台应用程序注册了 HandlerRoutine通过使用 SetConsoleCtrlHandler ,因此它可以在退出之前进行一些清理。
这让我能够对 CTRL+C
使用react/CTRL+BREAK
, ALT+F4
并使用 X
关闭控制台按钮。
遗憾的是,当任务管理器在单击 End Task
后尝试终止应用程序时,不会调用 HandlerRoutine。在 Process
选项卡,即使 documentation for HandlerRoutine声明以下关于 CTRL_CLOSE_EVENT
:
A signal that the system sends to all processes attached to a console when the user closes the console (either by clicking Close on the console window's window menu, or by clicking the End Task button command from Task Manager).
在这种情况下,我是否遗漏了某些内容或者文档有误?还有其他方法可以处理 End Task
?
我也尝试了 AppDomain.CurrentDomain.ProcessExit
事件,但其行为方式相同。
关于End Task
按钮 Details
任务管理器的 选项卡:据我了解,这个肯定无法处理,因为它会立即终止进程。
下面您可以找到一个 MWE 来重现该问题。我在 Windows 10 版本 20H2 上运行它。
// requires NuGet package: System.Windows.Extensions
using System.Media;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
namespace GracefulTermination.ConsoleDotNet5
{
class Program
{
private static readonly TaskCompletionSource terminationTcs = new TaskCompletionSource();
private static readonly HandlerRoutine handlerRoutine = HandleConsoleCtrl;
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private delegate bool HandlerRoutine(CtrlType dwCtrlType);
private enum CtrlType : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
static async Task Main()
{
SetConsoleCtrlHandler(handlerRoutine, add: true);
await terminationTcs.Task;
}
// FIXME not called when using "End Task" in "Process" tab of Task Manager
private static bool HandleConsoleCtrl(CtrlType dwCtrlType)
{
SystemSound sound = dwCtrlType == CtrlType.CTRL_CLOSE_EVENT
? SystemSounds.Hand
: SystemSounds.Asterisk;
sound.Play();
terminationTcs.SetResult();
return false;
}
[DllImport("Kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleCtrlHandler(
HandlerRoutine handler,
[MarshalAs(UnmanagedType.Bool)]
bool add
);
}
}
编辑 1
我做了一些更多的实验,结果发现这种行为的变化取决于调用“结束任务”的确切进程。
上图显示了我的应用程序在 PowerShell
中运行时的进程结构,但是cmd.exe
行为方式相同。
单击“结束任务”后不调用 HandlerRoutine 的进程标记为
🔴 红色以及确实调用它的标记为
🟢绿色。
Host für Konsolenfenster
是 conhost.exe
,我并没有试图终止它。
我还应该补充一点,随机结束标记为 🔴 红色的进程确实会触发处理程序一次尝试一次,但这种情况非常罕见。
对我来说,处理父进程( Windows PowerShell (3)
)的终止是最关心的问题,因为它是用户最有可能选择的进程。
但如果可能的话,我希望能够处理所有情况。
编辑2
documentation for SetConsoleCtrlHandler还指出以下内容:
The system generates CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT signals when the user closes the console, logs off, or shuts down the system so that the process has an opportunity to clean up before termination. Console functions, or any C run-time functions that call console functions, may not work reliably during processing of any of the three signals mentioned previously.
因此,我替换了 Console.Beep()
在 SystemSound.Play()
的示例中,因此清理期间没有使用控制台函数。
编辑3
编码似乎不是问题,因为 HandleConsoleCtrl()
正在被调用甚至收到 CTRL_CLOSE_EVENT
当使用 X
关闭控制台时按钮。
使用 End Task
时它不起作用。
尽管如此,我确实按照 Stephen Cleary 的建议改进了编码。 .
最佳答案
您的编码看起来不太正确。这是我使用的:
private enum ConsoleControlEvent : uint
{
CTRL_C_EVENT = 0,
CTRL_CLOSE_EVENT = 2,
CTRL_SHUTDOWN_EVENT = 6,
}
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private delegate bool SetConsoleCtrlHandler_HandlerRoutine(ConsoleControlEvent controlType);
[DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetConsoleCtrlHandler(SetConsoleCtrlHandler_HandlerRoutine handler,
[MarshalAs(UnmanagedType.Bool)] bool add);
关于c# - 如何处理 .NET 5 控制台应用程序中任务管理器中的 "End Task"?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65710454/