c# - 如何处理 .NET 5 控制台应用程序中任务管理器中的 "End Task"?

标签 c# .net winapi console exit

我的 .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

我做了一些更多的实验,结果发现这种行为的变化取决于调用“结束任务”的确切进程。

enter image description here

上图显示了我的应用程序在 PowerShell 中运行时的进程结构,但是cmd.exe行为方式相同。 单击“结束任务”后调用 HandlerRoutine 的进程标记为 🔴 红色以及确实调用它的标记为 🟢绿色。 Host für Konsolenfensterconhost.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/

相关文章:

c++ - 改进WM_PAINT和WM_CTLCOLORSTATIC处理程序的代码

c# - 如何用 XDT 转换替换字符串

c# - 给对象唯一的 ID onStart

c# - n层应用中引用的正确方法

c# - .NET 版本之间的差异(主要是 c#)

.net - 支持惰性求值的 .Net 正则表达式库

c++ - win32 应用程序中的操纵杆,winmm

c# - 替换 Windows 窗体中默认的未处理错误对话框

c# - 如何制作自己的事件处理程序?

winapi - Vista 上的 IPC(服务和应用程序)