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

8
有没有办法阻止.NET控制台应用程序被关闭?我有一个遵循以下模式的应用程序:
while (true)
{
    string x = Console.ReadLine();
    StartLongRunningTaskOnSeparateThread(x);
}

问题在于关闭控制台窗口是可能的(因此会中断长时间运行的任务)。是否有与Forms.OnClosing事件相当的控制台应用程序等效事件?
编辑-我不想创建一个不可能被杀死的东西,只是给出一个警告信息,例如“嘿,我还没有完成。你确定要关闭我吗?”
编辑2-防止通过'x'按钮过早退出比阻止Ctrl-C更重要(我使用了Console.TreatControlCAsInput = true;)。为此假设任何启动任务管理器的人都非常想杀死该程序。最终用户宁愿看到警告而不是意外取消他们的长时间运行任务。

根据EDIT2,如果是这种情况,只需从窗口中删除“X”按钮。请参见下面我更新的答案。 - beach
7个回答

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
    }
}

@SM - 如答案所述。但我给了你一个攻击计划。 ;) - beach
@SM - 移除了“X”按钮。但任务管理器仍然可以终止它。 - beach
“X”按钮非常有效,对于我的需求来说正是我所需要的东西。谢谢。 - aboy021

3
这并不是绝对可靠的,因为任务管理器或关机操作足以停止运行中的任务。但如果您将应用程序编写为服务,则至少不会出现诱人点击"X"的桌面应用?
根据您的评论,需要用户输入的控制台应用程序始终可以使用 "x" 关闭。将其重写为服务并非一项巨大的任务。服务实际上只是带有一些包装器代码的可执行文件。
难点在于用户输入。但是,您可以将长时间运行的线程编写为服务,然后使用控制台应用程序传递指令以运行该线程。
或者,如果您只是想防止意外按下 "x",为何不在控制台应用程序的前10行填写"IMPORTANT PROCESS DO NOT CLOSE!"?

  1. 不想重写
  2. 服务不好,因为需要用户输入
- SillyMonkey

3
为什么不编写一个小型的Winforms前端?这样当用户关闭时,可以更容易地提供视觉反馈。您可以禁用最小化/最大化/关闭按钮。
我的意思是,在运行超薄的winforms应用程序时,没有任何惩罚,它可以执行您的任务。
我假设您正在使用Windows,但如果使用Mono.NET,则可以使用GTK或类似工具。

1
有没有办法阻止.NET控制台应用程序关闭?
没有。您可以阻塞控制-C(请参见Console.CancelKeyPress事件),但无法阻止控制-中断。
如果您需要一个难以停止的东西,那么您需要考虑创建一个Windows服务,并使用ACL阻止其被停止(除非是由系统...因此系统可以关闭)。
补充说明:如果需要用户交互,则将应用程序分成两部分。一个保持运行状态并公开进程间入口点的服务。另一个提供交互性并使用进程间通信与服务进行通信的UI。

1
我不会阻止用户关闭应用程序。我会在启动进程时给他们一个警告,说“这可能需要几分钟时间,请等待...”

然后,如果他们决定终止它,他们就可以终止它。

个人而言,当一个进程太过控制我时,我会感到不喜欢。


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

    }

0

我不知道在控制台应用程序中是否有任何等效的Forms.OnClosing函数。但是,即使是Forms.OnClosing也不能完全防止应用程序关闭。我可以轻松地在任务管理器中找到程序并终止它。这将完全绕过Forms.OnClosing技巧并粗鲁地关闭您的应用程序。没有什么可以阻止这种情况发生。

你能给我们更多关于为什么想要这样做的上下文吗?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接