C # как получать события закрытия или выхода системы в приложении командной строки

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

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

Но я просто не могу этого понять.


person A.Grandt    schedule 16.05.2012    source источник
comment
Вы хотите, чтобы другое приложение обнаруживало, что это приложение закрыто, или вы хотите, чтобы это приложение само обнаруживало, что оно закрывается?   -  person Matthew Watson    schedule 16.05.2012
comment
это будет служба WCF или служба Windows   -  person Tilak    schedule 16.05.2012
comment
@MatthewWatson Это будет приложение, которое само обнаруживает, что оно закрывается либо системой, либо нажатием красного X в окне консоли.   -  person A.Grandt    schedule 16.05.2012
comment
@Tilak Вот разница. Планируется, что он будет работать на сервере Windows, но до того, как мне придется приступить к реализации этого бита, осталось несколько недель. Насколько я помню, он предоставляет методы запуска и остановки.   -  person A.Grandt    schedule 16.05.2012
comment
Я считаю, что вы можете просто обработать событие выхода из консольного приложения.   -  person Joshua Drake    schedule 16.05.2012
comment
@JoshuaDrake Я тоже пробовал, он не срабатывает вовремя. Комментарии к этой и другим ссылкам, кажется, предполагают, что моя установка не выиграет от этого, так как у меня просто нет времени, чтобы изящно завершить работу.   -  person A.Grandt    schedule 16.05.2012


Ответы (4)


Вот идеальное решение для того, что вам нужно:

Обнаружение выхода консольного приложения в c #

Код обнаруживает все возможные события, которые закроют приложение:

  • Ctrl + C
  • Ctrl + Break
  • Close Event (обычное закрытие программы с помощью кнопки Close)
  • Событие выхода (используйте выход из системы)
  • Событие выключения (закрытие системы)
namespace Detect_Console_Application_Exit2
{
  class Program
  {
    private static bool isclosing = false;

    static void Main(string[] args)
    {
      SetConsoleCtrlHandler(new HandlerRoutine(ConsoleCtrlCheck), true);
      Console.WriteLine("CTRL+C,CTRL+BREAK or suppress the application to exit");
      while (!isclosing) ;
    }

    private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
    {
      // Put your own handler here
      switch (ctrlType)
      {
        case CtrlTypes.CTRL_C_EVENT:
          isclosing = true;
          Console.WriteLine("CTRL+C received!");
          break;

        case CtrlTypes.CTRL_BREAK_EVENT:
          isclosing = true;
          Console.WriteLine("CTRL+BREAK received!");
          break;

        case CtrlTypes.CTRL_CLOSE_EVENT:
          isclosing = true;
          Console.WriteLine("Program being closed!");
          break;

        case CtrlTypes.CTRL_LOGOFF_EVENT:
        case CtrlTypes.CTRL_SHUTDOWN_EVENT:
          isclosing = true;
          Console.WriteLine("User is logging off!");
          break;
      }
      return true;
    }

    #region unmanaged

    // Declare the SetConsoleCtrlHandler function
    // as external and receiving a delegate.
    [DllImport("Kernel32")]
    public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

    // A delegate type to be used as the handler routine
    // for SetConsoleCtrlHandler.
    public delegate bool HandlerRoutine(CtrlTypes CtrlType);

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

    #endregion

    }
}
person JotaBe    schedule 16.05.2012
comment
Выглядит интересно, но вызывает исключение извне, и я не получаю событие в приложении C #. - person A.Grandt; 16.05.2012
comment
Где вы получаете исключение и какое исключение? - person JotaBe; 16.05.2012
comment
Я получаю только диалог AppNamehas перестал работать. - person A.Grandt; 16.05.2012
comment
Обратный вызов был выполнен для делегата типа «AppName! Proxy.AppName + HandlerRoutine :: Invoke» со сборкой мусора. Это может вызвать сбои приложения, повреждение и потерю данных. При передаче делегатов неуправляемому коду они должны поддерживаться управляемым приложением до тех пор, пока не будет гарантировано, что они никогда не будут вызваны. - person A.Grandt; 16.05.2012
comment
Насколько я могу судить, на самом деле нет изящного способа сделать это. Я уже сделал так, что нажатие клавиши Enter в консоли отключает ее правильно, и в основном это сделано для того, чтобы не ждать более 30 минут каждый раз, когда я облажаюсь и быстро убиваю приложение :) - person A.Grandt; 16.05.2012
comment
@ A.Grandt, вы запускаете его с отладкой или автономно? - person Joshua Drake; 16.05.2012
comment
Я только что скомпилировал и запустил приложение, которое отлично работает с этим кодом. В этом случае ConsoleControlCheck (обработчик событий) - это статический метод программы, поэтому он будет существовать, пока приложение активно. Попробуйте аналогичный путь. Он работает на моей машине, но я уверен, что он будет работать и на вашей! - person JotaBe; 16.05.2012
comment
@JoshuaDrake У вас есть смысл, я запускаю его из Visual Studio в режиме без отладки. - person A.Grandt; 17.05.2012
comment
@ A.Grandt попробуйте запустить его самостоятельно, после того, как он встроен в Release. - person Joshua Drake; 17.05.2012
comment
Я могу запустить его как в режиме отладки, так и в режиме выпуска. - person JotaBe; 18.05.2012
comment
Вроде проблема с таймингом. Мне просто не хватает времени после того, как выключение началось. Мне нужно как минимум 10-15 секунд для плавного выключения. Поскольку это действительно просто для предотвращения проблем во время тестирования и начальной разработки, я думаю, что мне будет лучше просто убедиться, что люди не закрывают приложение неправильно, проблема должна быть автоматически решена, как только мы перейдем на службу Windows. - person A.Grandt; 18.05.2012
comment
Спасибо всем, кто хотя бы пытался помочь, информация наверняка позже пригодится. - person A.Grandt; 18.05.2012
comment
@ A.Grandt Когда вы переходите к службе Windows, вы можете обработать событие остановки. Если вы хотите добиться успеха, вам следует запустить рабочий поток и управлять им из обработчика события остановки. То есть, когда поступает запрос на остановку, обработчик событий должен уведомить рабочий поток о том, что он должен остановиться, и дождаться его остановки перед закрытием службы. Поскольку служба закрывается, когда код в обработчике события остановки завершает выполнение, вам необходимо использовать какой-то механизм синхронизации, чтобы уведомить рабочий поток о том, что он должен остановиться, и дождаться его остановки. - person JotaBe; 18.05.2012

Попробуй это:

[РЕДАКТИРОВАТЬ] Это измененная версия по ссылке, размещенной выше.

ПРИМЕЧАНИЕ. Если пользователь щелкает красный значок X, чтобы закрыть окно консоли, у вас есть ОЧЕНЬ ограниченное время, чтобы ответить, прежде чем ваше приложение будет убито! Если вы запустите следующую программу, посмотрите, как долго отображается окно сообщения, прежде чем оно исчезнет.

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;


namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            SetConsoleCtrlHandler(ConsoleCtrlCheck, true);
            Console.WriteLine("CTRL+C,CTRL+BREAK or suppress the application to exit");

            while (!isclosing)
            {
                Thread.Sleep(1000);
            }
        }

        private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
        {
            // Put your own handling here:

            switch (ctrlType)
            {
                case CtrlTypes.CTRL_C_EVENT:

                    isclosing = true;

                    Console.WriteLine("CTRL+C received!");

                    break;

                case CtrlTypes.CTRL_BREAK_EVENT:

                    isclosing = true;

                    Console.WriteLine("CTRL+BREAK received!");

                    break;

                case CtrlTypes.CTRL_CLOSE_EVENT:

                    isclosing = true;

                    Console.WriteLine("Program being closed!");
                    MessageBox.Show("AHA!");

                    break;

                case CtrlTypes.CTRL_LOGOFF_EVENT:
                case CtrlTypes.CTRL_SHUTDOWN_EVENT:

                    isclosing = true;

                    Console.WriteLine("User is logging off!");

                    break;
            }

            return true;
        }

        #region unmanaged

        // Declare the SetConsoleCtrlHandler function as external and receiving a delegate.

        [DllImport("Kernel32")]

        public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);

        // A delegate type to be used as the handler routine for SetConsoleCtrlHandler.

        public delegate bool HandlerRoutine(CtrlTypes CtrlType);

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

        #endregion

        private static bool isclosing;
    }
}
person Matthew Watson    schedule 16.05.2012
comment
По какой-то причине это не удается. Смотрите мои комментарии к первому ответу, чтобы узнать, что я получил. - person A.Grandt; 16.05.2012
comment
Вероятно, потому что процесс завершается до того, как ваш обработчик завершит работу должным образом. Вам понадобится что-то, что реагирует очень быстро, например, EventWaitHandle, который вы просто сигнализируете, когда программа закрывается. - person Matthew Watson; 17.05.2012
comment
@ A.Grandt - Никакое решение на основе SetConsoleCtrlHandler не будет работать, если ваша программа ссылается на gdi.dll или user32.dll (в этом случае обратный вызов не будет вызван). - person Jirka Hanika; 23.06.2018

Если событие обрабатывается внутри приложения,

  1. Если это служба Windows,

    Можно использовать ServiceBase.OnStop

  2. Если это служба WCF, ServiceHost.Closed событие можно использовать. ServiceHost наследует Closed (и связанные события) от узла связи

Если событие должно обрабатываться в отдельном приложении, Process.Exited можно использовать.

var serviceProcesses = Process.GetProcessesByName("service.exe");
if(serviceProcesses != null && serviceProcesses.Length>0)
{
    serviceProcesses[0].Exited += OnServiceClosed;
}
person Tilak    schedule 16.05.2012
comment
Я вернусь к этому, когда начну работать с Сервисом :) - person A.Grandt; 16.05.2012

AppDomain.CurrentDomain.ProcessExit событие?

person Val Bakhtin    schedule 16.05.2012