Остановить консольное приложение .net от закрытия - PullRequest
6 голосов
/ 18 мая 2009

Есть ли способ остановить закрытие консольного приложения .NET? У меня есть приложение, которое следует этому шаблону:

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

Проблема в том, что можно закрыть окно консоли (и, следовательно, обрезать долгосрочное задание). Есть ли эквивалент события Forms.OnClosing для консольных приложений?

РЕДАКТИРОВАТЬ - Я не пытаюсь создать что-то, что невозможно убить, просто, возможно, выдайте предупреждающее сообщение, например: "Эй, я еще не закончил. Вы уверены, что хотите закрыть меня?"

EDIT2 - Предотвращение преждевременного выхода с помощью кнопки 'x' важнее, чем блокировка Ctrl-C (я использовал Console.TreatControlCAsInput = true;). Предположим для этой цели, что любой, кто запускает диспетчер задач, хочет убить программу так сильно, что заслуживает того, чтобы иметь возможность. И что конечный пользователь скорее увидит предупреждение, чем случайно отменит свою долгосрочную задачу.

Ответы [ 7 ]

10 голосов
/ 18 мая 2009

Вот моя попытка решить эту проблему. Диспетчер задач все еще может закрыть приложение. Но я думал попытаться определить, когда оно закрывается, а затем снова запустить его. Однако я не реализовал эту часть.

Следующая программа обнаружит 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
    }
}
3 голосов
/ 18 мая 2009

Это не надежно, поскольку очевидно, что диспетчер задач или завершение работы достаточно, чтобы остановить задачу, однако, если бы вы написали приложение в качестве службы, то, по крайней мере, оно не будет сидеть на рабочем столе с заманчивым «x» людей бить?

РЕДАКТИРОВАТЬ: на основе вашего комментария. Консольное приложение, требующее пользовательского ввода, который вы не можете переписать, всегда будет закрыто с помощью «x».

Переписать как сервис - не большая задача. Сервис - это не более чем исполняемый файл с некоторым кодом оболочки.

Сложность связана с пользовательским вводом. Тем не менее, вы могли бы написать свой долго работающий поток как сервис, но иметь консольное приложение для передачи инструкции по запуску потока.

В качестве альтернативы, если вы просто хотите, чтобы люди случайно не нажимали «x», почему бы не заполнить первые 10 строк консольного приложения «ВАЖНЫЙ ПРОЦЕСС НЕ ЗАКРЫТЬ !!»

2 голосов
/ 18 мая 2009

Почему бы вам не написать небольшой интерфейс Winforms? Проще обеспечить визуальную обратную связь, когда пользователь закрывается. Вы можете отключить де минимизировать / развернуть / закрыть кнопки.

Я имею в виду, что за запуск сверхтонкого приложения winforms, которое может выполнить ваши вещи, нет штрафов.

Я предполагаю, что вы работаете в Windows, но если бы это был Mono.NET, вы могли бы использовать GTK или аналогичный.

1 голос
/ 18 мая 2009

Есть ли способ остановить закрытие консольного приложения .NET?

Нет. Вы можете заблокировать control-C (см. Событие Console.CancelKeyPress), но не control-break.

Если вам нужно что-то, что трудно остановить, вам нужно взглянуть на создание службы Windows, с ACL, чтобы заблокировать его остановку (кроме системы ... чтобы система могла выключиться).

Дополнение: если требуется взаимодействие с пользователем, разделите приложение на две части. Служба, которая продолжает работать, открывая точку входа между процессами. И пользовательский интерфейс, который обеспечивает интерактивность и использует межпроцессное взаимодействие для связи со службой.

0 голосов
/ 09 августа 2013
    [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 голосов
/ 18 мая 2009

Я бы не запретил пользователю закрывать приложение. Я хотел бы предупредить их, когда вы начнете процесс: «Это может занять несколько минут. Пожалуйста, подождите ...»

Тогда, если они решат убить его, они убьют его.

Лично мне не нравится, когда процесс отнимает у меня слишком много контроля.

0 голосов
/ 18 мая 2009

Я не знаю ни одной эквивалентной функции Forms.OnClosing для консольного приложения. Однако даже Forms.OnClosing не является надежным способом предотвратить закрытие приложения. Я могу так же легко найти программу в диспетчере задач и убить ее. Это полностью обойдет трюк с Forms.OnClosing и грубо закроет ваше приложение. Вы ничего не можете сделать, чтобы предотвратить это.

Можете ли вы дать нам немного больше информации о том, почему вы хотите это сделать?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...