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

时间:2009-05-18 13:50:31

标签: .net console

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

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

问题是可以关闭控制台窗口(因此切断长时间运行的任务)。控制台应用程序的Forms.OnClosing事件是否等效?

编辑 - 我不是想创造一些不可能杀人的东西,只是给一个警告信息e..g“嘿,我还没有完成。你确定要关闭我吗?”

EDIT2 - 通过“x”按钮防止不合时宜的退出比阻止Ctrl-C(我使用Console.TreatControlCAsInput = true;)更重要。假设为此目的,任何启动任务管理器的人都想要杀死程序,以至于他们应该能够。并且最终用户宁愿看到警告而不是意外取消他们长时间运行的任务。

7 个答案:

答案 0 :(得分:10)

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

以下程序会检测到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
    }
}

答案 1 :(得分:3)

这并非万无一失,因为显然任务管理器或关机足以停止任务,但是,如果您将应用程序编写为服务,那么至少它不会坐在桌面上,有诱人的“x”有人打?

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

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

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

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

答案 2 :(得分:2)

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

我的意思是,运行一个可以执行你的东西的超级winforms应用程序没有任何惩罚。

我假设你在Windows上,但如果这是Mono.NET,你可以使用GTK或类似的。

答案 3 :(得分:1)

  

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

没有。您可以阻止control-C(请参阅Console.CancelKeyPress事件),但不能阻止控制中断。

如果您需要一些难以阻止的东西,您需要查看创建Windows服务,使用ACL来阻止它被阻止(系统除外......因此系统可以关闭)。

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

答案 4 :(得分:0)

我不知道控制台应用程序的Forms.OnClosing的任何等效功能。然而,即使Forms.OnClosing也不是防止应用程序关闭的万无一失的方法。我可以在任务管理器中轻松找到该程序并将其删除。这将完全绕过Forms.OnClosing技巧并粗暴地关闭您的应用程序。你无能为力阻止这一点。

您能否就我们为什么要这样做提供更多背景信息?

答案 5 :(得分:0)

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

然后,如果他们决定杀死它,他们会杀死它。

当一个过程从我身上夺走太多控制权时,我个人不喜欢它。

答案 6 :(得分:0)

    [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);

    }