代码之家  ›  专栏  ›  技术社区  ›  SillyMonkey

停止关闭.NET控制台应用程序

  •  6
  • SillyMonkey  · 技术社区  · 16 年前

    有没有办法阻止.NET控制台应用程序关闭?我有一个应用程序遵循这个模式:

    while (true)
    {
        string x = Console.ReadLine();
        StartLongRunningTaskOnSeparateThread(x);
    }
    

    问题是有可能关闭控制台窗口(从而切断长时间运行的任务)。控制台应用程序的Forms.onClosing事件是否与之类似?

    编辑-我不是想创造一些不可能杀死的东西,只是可能会发出警告信息,例如“嘿,我还没完成”。你确定要关闭我吗?”

    edit2-通过“x”按钮防止过早退出比阻止ctrl-c更重要(我使用 Console.TreatControlCAsInput = true; )为此,假设任何激发任务管理器的人都想杀死程序,以至于他们应该能够杀死程序。最终用户宁愿看到警告也不愿意外地取消他们长期运行的任务。

    7 回复  |  直到 12 年前
        1
  •  10
  •   beach    16 年前

    这是我试图解决这个问题的方法。不过,任务管理器仍然可以关闭应用程序。但是,我的想法是尝试检测它何时关闭,然后重新启动。但是,我没有实现那个部分。

    以下程序将检测到一个ctrl-c&ctrl-break并将继续运行。

    编辑: 已删除“X”按钮

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.ComponentModel;
    using System.Runtime.InteropServices;
    using System.Diagnostics;
    
    namespace DoNotCloseMe
    {
        class Program
        {
    
            const string _title = "DO NOT CLOSE - Important Program";
    
            static void Main(string[] args)
            {
    
                Console.Title = _title;
    
                IntPtr hMenu = Process.GetCurrentProcess().MainWindowHandle;
                IntPtr hSystemMenu = GetSystemMenu(hMenu, false);
    
                EnableMenuItem(hSystemMenu, SC_CLOSE, MF_GRAYED);
                RemoveMenu(hSystemMenu, SC_CLOSE, MF_BYCOMMAND);
    
                WriteConsoleHeader();
    
                //This function only seems to be called once.
                //After calling MainLoop() below, CTRL-C && CTRL-BREAK cause Console.ReadLine() to return NULL 
                Console.CancelKeyPress += (sender, e) =>
                {
                    Console.WriteLine("Clean-up code invoked in CancelKeyPress handler.");
                    Console.WriteLine("Key Pressed: {0}", e.SpecialKey.ToString());
                    System.Threading.Thread.Sleep(1000);
                    MainLoop();
                    // The application terminates directly after executing this delegate.
                };
    
                MainLoop();
    
            }
    
            private static void MainLoop()
            {
                while (true)
                {
                    WriteConsoleHeader();
                    string x = Console.ReadLine();
                    if (!String.IsNullOrEmpty(x))
                    {
                        switch (x.ToUpperInvariant())
                        {
                            case "EXIT":
                            case "QUIT":
                                System.Environment.Exit(0);
                                break;
                            default:
                                StartLongRunningTaskOnSeparateThread(x);
                                break;
                        }
    
                    }
                }
            }
    
            private static void StartLongRunningTaskOnSeparateThread(string command)
            {
                var bg = new System.ComponentModel.BackgroundWorker();
                bg.WorkerReportsProgress = false;
                bg.WorkerSupportsCancellation = false;
    
                bg.DoWork += (sender, args) =>
                {
                    var sleepTime = (new Random()).Next(5000);
                    System.Threading.Thread.Sleep(sleepTime);
                };
    
    
                bg.RunWorkerCompleted += (sender, args) =>
                {
                    Console.WriteLine("Commmand Complete: {0}", command);
                };
    
                bg.RunWorkerAsync();
    
            }
    
            private static void WriteConsoleHeader()
            {
                Console.Clear();
                Console.WriteLine(new string('*', Console.WindowWidth - 1));
                Console.WriteLine(_title);
                Console.WriteLine(new string('*', Console.WindowWidth - 1));
                Console.WriteLine("Please do not close this program.");
                Console.WriteLine("It is maintaining the space/time continuum.");
                Console.WriteLine("If you close it, Q will not be happy and you will be assimilated.");
                Console.WriteLine(new string('*', Console.WindowWidth - 1));
                Console.WriteLine("Development Mode: Use \"EXIT\" or \"QUIT\" to exit application.");
                Console.WriteLine(new string('*', Console.WindowWidth - 1));
            }
    
            #region "Unmanaged"
    
            [DllImport("user32.dll")]
            static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);
    
            [DllImport("user32.dll")]
            static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
    
            [DllImport("user32.dll")]
            static extern IntPtr RemoveMenu(IntPtr hMenu, uint nPosition, uint wFlags);
    
            internal const uint SC_CLOSE = 0xF060;
            internal const uint MF_GRAYED = 0x00000001;
            internal const uint MF_BYCOMMAND = 0x00000000;
    
            #endregion
        }
    }
    
        2
  •  3
  •   Robin Day    16 年前

    很明显,任务管理器或关机足以停止任务,这不是简单的,但是,如果您将应用程序作为服务来编写,那么至少它不会坐在桌面上,上面有一个诱人的“x”供人们点击?

    编辑:基于您的评论。 需要用户输入但无法重写的控制台应用程序始终可以使用“x”关闭。

    作为服务重写并不是一项艰巨的任务。服务实际上只是一个带有一些包装代码的可执行文件。

    困难在于用户的输入。但是,您可以将长时间运行的线程编写为一个服务,同时还可以使用一个控制台应用程序来传递运行该线程的指令。

    或者,如果你只是想阻止人们不小心按下“X”,为什么不在控制台应用程序的前10行填写“重要过程不要关闭”!“

        3
  •  2
  •   Martin Marconcini    16 年前

    为什么不写一个小的WinForms前端?当用户关闭时,更容易提供视觉反馈。您可以禁用取消最小化/最大化/关闭按钮。

    我的意思是,运行一个超薄的WinForms应用程序来执行你的任务并没有什么损失。

    我假设你在Windows上,但是如果这是mono.net,你可以使用gtk或类似的工具。

        4
  •  1
  •   Richard    16 年前

    有没有办法阻止.NET控制台应用程序关闭?

    不,您可以阻止Control-C(请参见 Console.CancelKeyPress 事件),但不是控制中断。

    如果您需要一些难以停止的东西,您需要查看创建一个Windows服务,并使用一个ACL阻止它停止(系统除外…这样系统就可以关机了)。

    附加:如果需要用户交互,那么将应用程序分为两部分。保持运行的服务,公开进程间入口点。以及提供交互性并使用进程间通信与服务通信的UI。

        5
  •  0
  •   JaredPar    16 年前

    对于控制台应用程序,我不知道Forms.onClosing的任何等效功能。然而,即使是forms.onclosing也不是防止应用程序关闭的简单方法。我也可以很容易地在任务管理器中找到程序并杀死它。这将完全绕过Forms.onClosing技巧并粗暴地关闭应用程序。你无能为力阻止这一切。

    你能给我们一点你为什么要这样做的背景吗?

        6
  •  0
  •   David Basarab    16 年前

    我不会阻止用户关闭应用程序。我要做的是,当你开始这个过程时,给他们一个警告,说“这可能需要几分钟。请等一下……”

    如果他们决定杀了它,他们就杀了它。

    就我个人而言,我不喜欢一个过程剥夺了我太多的控制权。

        7
  •  0
  •   Ken Salter    12 年前
        [DllImport("Kernel32")]
        public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
    
        // 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
        }
    
        // A delegate type to be used as the handler routine 
        // for SetConsoleCtrlHandler.
        public delegate bool HandlerRoutine(CtrlTypes CtrlType);
    
        private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
        {
            Trace.WriteLine("** Received termination event : " + ctrlType.ToString());
            return true;
        }
    
        static void Main(string[] args)
        {
    
            SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
    
        }