2

我们可以一起努力想出一些适用于 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 服务”答案之一。

4

3 回答 3

3

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

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

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

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

于 2012-09-22T11:57:46.443 回答
1

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

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

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

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

您要记录的一件事将是“我当时干净地关闭了T”。如果您重新启动并且在日志末尾没有找到该项目,您就知道出了点问题,您可以采取任何需要的操作。

如果您需要知道您的服务在做什么,请使用众多日志 框架之一将事件传送到第二个应用程序,该应用程序仅显示活动。

于 2012-09-22T11:54:43.197 回答
0

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

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 发送给它,如下所示。为什么关闭使用 AllocConsole 启动的控制台会导致我的整个应用程序退出?我可以改变这种行为吗?

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

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

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

于 2014-02-01T22:33:30.397 回答