【问题标题】:The definitive code that prevents a c# console app from exiting [until custom cleanup code has completed]阻止 c# 控制台应用程序退出的最终代码 [直到自定义清理代码完成]
【发布时间】:2012-09-14 14:28:42
【问题描述】:

我们可以一起工作,想出一些适用于 control-c、control-break、注销、按下窗口 X 按钮等的东西吗?

这是我目前所拥有的:

class Program
{  
    private static ConsoleEventHandlerDelegate consoleHandler;
    delegate bool ConsoleEventHandlerDelegate(CtrlTypes eventCode);

    static void Main(string[] args)
    {
        consoleHandler = new ConsoleEventHandlerDelegate(ConsoleCtrlCheck);
        SetConsoleCtrlHandler(consoleHandler, true);

        System.Diagnostics.Process.GetCurrentProcess().Exited 
           += delegate(object sender, EventArgs e) 
        {              
            GeneralManager.Stop();
        };

        Console.CancelKeyPress += delegate(object sender,
                                ConsoleCancelEventArgs e)
        {
            e.Cancel = false;
            GeneralManager.Stop();
        };

        GeneralManager.Start();
    }

    private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
    {
        switch (ctrlType)
        {                
            case CtrlTypes.CTRL_C_EVENT:

                Console.WriteLine("CTRL+C received!");
                GeneralManager.Stop();
                break;

            case CtrlTypes.CTRL_BREAK_EVENT:
                isclosing = true;
                Console.WriteLine("CTRL+BREAK received!");
                GeneralManager.Stop();
                break;

            case CtrlTypes.CTRL_CLOSE_EVENT:

                Console.WriteLine("Program being closed!");
                GeneralManager.Stop();
                break;

            case CtrlTypes.CTRL_LOGOFF_EVENT:
            case CtrlTypes.CTRL_SHUTDOWN_EVENT:

                Console.WriteLine("User is logging off!");
                GeneralManager.Stop();
                break;                           
        }
        return true;
    }

    #region unmanaged

            [DllImport("kernel32.dll")]
          static extern bool SetConsoleCtrlHandler(ConsoleEventHandlerDelegate
            handlerProc, bool add);

    public delegate bool HandlerRoutine(CtrlTypes CtrlType);

    public enum CtrlTypes
    {
        CTRL_C_EVENT = 0,
        CTRL_BREAK_EVENT,
        CTRL_CLOSE_EVENT,
        CTRL_LOGOFF_EVENT = 5,
        CTRL_SHUTDOWN_EVENT
    }

    #endregion
}

两个问题:

  1. 在 Managed Control-Break 处理程序中,如果我们设置 e.Cancel = true 它会失败并出现 .Net4 异常。这在 MSDN 文章中注明,但没有解决方法:http://msdn.microsoft.com/en-us/library/system.consolecanceleventargs.cancel.aspx

  2. 我不知道如何取消ConsoleCtrlCheck 中的关闭。我有一两秒钟的时间来做一些清理工作,但我宁愿取消并确保一切都正确完成。

更新:

感谢您的回复。两者都赞成。将等待看看是否有人可以提出直接解决我要求的答复,否则将接受“使用 NT 服务”答案之一。

【问题讨论】:

  • 一个程序在被告知退出时不退出称为恶意软件
  • Protip:不要尝试这个。这不值得。
  • 您假设它是一个客户端应用程序。它是用于服务器的,我想确保如果有人意外击中 X,则运行清理例程。另一种方法是将其作为服务运行......但我喜欢看到控制台。
  • “清理”到底是什么意思?关闭时可能需要执行一些逻辑,但您不必担心资源问题。
  • 你有一个开放的 TCP 服务器作为控制台运行,人们可能会关闭它?

标签: c# .net console-application shutdown resource-cleanup


【解决方案1】:

我没有尝试用控制台应用程序做这种事情,但你可能会用 Windows 窗体(或 WCF 应用程序)做得更好。他们会给你一个FormClosing 可以取消的活动。或者,如果您正在编写网络服务,请使用 Windows 服务,它提供了一个接口来干净地stop 您的应用程序。

如果您真的热衷于控制台应用程序,也许所有代码周围的 try {} finally {} 子句或像 critical finaliser 这样更奇特的东西可能允许您运行清理代码。但这确实不是适合这项工作的工具。

在某些情况下,您无法阻止应用程序被关闭,例如:电源故障或任务管理器终止命令(如果应用程序没有通过 X 关闭,任务管理器是我要使用的第一个工具)。

因此,对您的服务应用程序进行编码,以便将所有客户端请求都记录到事务日志中(就像 SQL Server 一样)。如果您被意外打断(无论在何种情况下),在此之前发生的任何事情都会记录在日志中。当您的服务下次启动时,重播该日志。

您要记录的内容之一是“我在时间T 完全关闭”。如果您重新启动,但在日志末尾没有找到该项目,您就知道出了问题,您可以采取任何必要的措施。

如果您需要知道您的服务在做什么,请使用众多 logging frameworks 之一将事件通过管道传输到仅显示活动的第二个应用程序。

【讨论】:

    【解决方案2】:

    我需要等待待处理的用户请求完成,彻底断开它们,在数据库上运行一些查询以反映状态的变化等等。它是一个 TCP 服务器。

    然后不要将其作为控制台或任何其他类型的客户端应用程序运行。

    只需将其作为 Windows (NT) 服务运行,您唯一需要担心的事件就是断电和停止信号。

    使用 UPS 并确保您可以在合理的时间内关闭。

    【讨论】:

      【解决方案3】:

      我花了几个小时看这个,因为我现在没有时间构建一个工作代码;虽然它可能很短,但要让它正确需要一段时间。我只会给你链接到完成这项工作所需的各种东西:

      http://pastebin.com/EzX3ezrf

      从贴中的代码总结经验教训:

      1. 需要一个消息泵来处理部分/全部 WM_QUERYENDSESSION、WM_ENDSESSION、CTRL_SHUTDOWN_EVENT(在 c# 中 SystemEvents.SessionEnding 可能涵盖部分/全部)

      2. 获取消息泵的最简单方法是使其成为隐藏的表单/窗口应用程序,但我记得可以构建为控制台应用程序并添加消息泵。不过,我没有在粘贴中包含该代码。

      3. “如果应用程序必须阻止潜在的系统关闭,它可以调用 ShutdownBlockReasonCreate 函数”

      4. 由于 AllocConsole 用于创建控制台,您需要使用 SetConsoleCtrlHandler 并在处理程序中使用 ExitThread(1)。这是一个“hack”,它会杀死关闭控制台的线程。它在 FarManager 中使用。例如,参见 interf.cpp

      5. 使用 AllocConsole 时还需要初始化和清理控制台。

      6. 报告按 CTRL+C 会打乱输入。我不确定 FarManager 是否正在处理这种情况。 interf.cpp 中的 CTRL_BREAK_EVENT 处理程序中有一些代码,我不确定它的作用。

      7. FarManager 还处理 WM_POWERBROADCAST,可能与挂起有关

      如果这还不够(应该),您还可以将控制台添加到另一个进程中,并将您的消息 IPC 发送给它,如下所示。 Why does closing a console that was started with AllocConsole cause my whole application to exit? Can I change this behavior?

      RMTool 可用于模拟注销/关闭消息以进行测试:http://download.microsoft.com/download/d/2/5/d2522ce4-a441-459d-8302-be8f3321823c/LogoToolsv1.0.msi

      MSDN 在 microsoft.win32.systemevents.sessionending.aspx 也有一些 C# 代码 和 microsoft.win32.systemevents.aspx(隐藏形式示例)

      mischel.com/pubs/consoledotnet/consoledotnet.zip 有一个示例 winTest 项目,其中使用了 AllocConsole 并处理了一些事件。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-12-08
        • 2013-09-17
        • 2016-08-29
        • 2017-11-12
        • 1970-01-01
        • 2016-01-04
        • 2010-09-14
        • 2011-02-01
        相关资源
        最近更新 更多