5

I'm trying, but everything I've found so far seems to centre around GUI's and form apps. That is not what I'm using.

Eventually it'll be a service, but till it is done I'm running it as a normal app, the problem being that if I just close it by accident, it'll leave another service hanging for up to 30 minutes with bad data. I though it would be easy to add a listener to catch the app being closed.

But I just can't figure it out.

A.Grandt
  • 2,242
  • 2
  • 20
  • 21
  • Do you want another app to detect that this app closed, or do you want this app itself to detect that it's being closed? – Matthew Watson May 16 '12 at 15:05
  • will it be WCF service or windows service – Tilak May 16 '12 at 15:06
  • @MatthewWatson It'll be the app itself detecting that it is being closed, either by the system, or by pressing the red X on the console window. – A.Grandt May 16 '12 at 15:08
  • @Tilak Waht is the difference. It is scheduled to run on a Windows server, but that is sill a few weeks off before I have to start implement that bit. If I recall, it does provide start and stop methods. – A.Grandt May 16 '12 at 15:09
  • I believe that you can simply handle the [Console Application Exit Event](http://stackoverflow.com/questions/1119841/net-console-application-exit-event). – Joshua Drake May 16 '12 at 15:13
  • @JoshuaDrake I tried that one as well, it is not firing in time. Comments on that link and others seem to suggest that my setup will not benefit from it, as I simply do not have the time to shut-down gracefully anyway. – A.Grandt May 16 '12 at 15:50

4 Answers4

5

Here you have a perfect solution for what you need:

Detecting console application exit in c#

The code detects all the possible events that will close the application:

  • Control+C
  • Control+Break
  • Close Event ("normal" closing of the program, with Close Button)
  • Logoff Event (use logging off)
  • Shutdown Event (system closing)
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

    }
}
JotaBe
  • 38,030
  • 8
  • 98
  • 117
  • Looks interesting, but it is causing an exception from the external and I'm not getting the event into the c# app – A.Grandt May 16 '12 at 15:28
  • Where are you getting the exception, and which exception? – JotaBe May 16 '12 at 15:32
  • I only get the "AppNamehas stopped working" dialog. – A.Grandt May 16 '12 at 15:33
  • A callback was made on a garbage collected delegate of type 'AppName!proxy.AppName+HandlerRoutine::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called. – A.Grandt May 16 '12 at 15:34
  • From what I can tell, there really are no graceful way of doing this. I've already made it so that pressing enter in the console shuts it down correctly, and most of this is just to prevent having to wait 30+ minutes every time I screw up and kill the app the fast way :) – A.Grandt May 16 '12 at 15:44
  • @A.Grandt are you running it with Debugging, or stand alone? – Joshua Drake May 16 '12 at 15:58
  • I have just compiled and run an app that works perfectly, with this code. In this case, the ConsoleControlCheck (event handler) is a static method of the program, so that it will exist while the app is alive. Try a similar path. "It works on my machine", but I'm sure it will also work in yours! – JotaBe May 16 '12 at 16:17
  • @JoshuaDrake You have a point, I'm running it from Visual Studio, in non-debugging mode. – A.Grandt May 17 '12 at 14:37
  • @A.Grandt try running it on its own, after having built it in Release. – Joshua Drake May 17 '12 at 14:43
  • I can run it in debug mode as well as in release mode. – JotaBe May 17 '12 at 23:49
  • It seems to be a problem with timing. I simply don't get enough time once the shutdown is initiated. I need at least 10-15 seconds for a graceful shutdown. As this is really just to prevent problems during testing and initial development, I think I'll be better off just making sure people do not shut down the app incorrectly, the problem should be automatically resolved once we move to a Windows service. – A.Grandt May 18 '12 at 09:42
  • Thanks to all who at least tried to assist, the information will most likely prove useful later. – A.Grandt May 18 '12 at 09:43
  • 2
    @A.Grandt When you move to a windows service, you can handle the stop event. If you want to be succesful, you should start a worker thread and control it from the stop event handler. I.e., when an stop request arrives, the event handler should notify the worker thread that it should stop, and wait until it stops, before closing the service. As the service closes when the code in the stop event handler finishes executing, you'll need to use some kind of synchronization mechanism to notify the worker thread that it should stop, and wait until it stops. – JotaBe May 18 '12 at 10:54
2

Try this:

[EDIT] This is a modified version from the link posted above.

NOTE: If the user clicks the red X to close the console window, you have a VERY limited time to respond before your app is killed! If you run the following program, look at how long the message box is displayed before it dies.

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;
    }
}
Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • For some reason this one fails. See my comments on the first answer for what I get. – A.Grandt May 16 '12 at 15:46
  • Probably because the process is killed before your handler finishes properly. You'd need to have something that responds very quickly, such as a named EventWaitHandle that you just signal when the program closes. – Matthew Watson May 17 '12 at 07:56
  • @A.Grandt - No solution based on SetConsoleCtrlHandler will work if your program is linking to gdi.dll or user32.dll (the callback will never be called in that case). – Jirka Hanika Jun 23 '18 at 05:31
1

If event to be handled inside the app,

  1. If it is windows service,

    ServiceBase.OnStop can be used

  2. If it is WCF service ServiceHost.Closed event can be used. ServiceHost inherit Closed (and related events) from Communication Host

If event is to be handled in separate app, Process.Exited event can be used.

var serviceProcesses = Process.GetProcessesByName("service.exe");
if(serviceProcesses != null && serviceProcesses.Length>0)
{
    serviceProcesses[0].Exited += OnServiceClosed;
}
Tilak
  • 30,108
  • 19
  • 83
  • 131
0

AppDomain.CurrentDomain.ProcessExit event?

Val Bakhtin
  • 1,434
  • 9
  • 11