1

This is not a duplicate of this post. I got my solution from this post but it does not work not to mention my app is a winforms app, not a console app.

So I have a project that is running in the background. Basically, I created a Windows forms application but I am not calling the Form in Program.cs. I created a class that listens to when the application exits:

class ShutDownManager
    {
        public ShutDownManager()
        {
            _handler += new EventHandler(Handler);
            SetConsoleCtrlHandler(_handler, true);
        }
        static bool exitSystem = false;


        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);

        private delegate bool EventHandler(CtrlType sig);
        static EventHandler _handler;

        enum CtrlType
        {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT = 1,
            CTRL_CLOSE_EVENT = 2,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT = 6
        }

        private static bool Handler(CtrlType sig)
        {
            Console.Writeline("I'M OUT OF HERE");


            return true;
        }

    }

I create an instance of this class in Program.cs's Main function. However this does not seem to fire. It works like a charm in Console applications but when it comes to my forms application (without the form) it doesn't work. What could be the problem?

My code in Main:

static void Main()
{
     ShutDownManager sdm = new ShutDownManager();
     StartUpManager.AddApplicationToCurrentUserStartup();
     Timers timers = new Timers();
}
user6879072
  • 441
  • 1
  • 7
  • 17
  • 1
    First of all: why a winforms app when you don't want to use winforms? And what is the difference to your console application that you need a winforms app? – Mighty Badaboom Aug 30 '17 at 12:40
  • @MightyBadaboom I want my app to run in the background and creating a winforms app without calling the form is automatically recognised as a background app by windows. – user6879072 Aug 30 '17 at 12:42
  • Do you understand how winform event loop works. Are you sure `SetConsoleCtrlHandler` will work without a console? Can you show your code in `Main`? – Vikhram Aug 30 '17 at 12:45
  • If that code is running in console and you don't need UI then why don't you run that console app in background itself. – Bhuban Shrestha Aug 30 '17 at 12:48
  • 1
    That is a very hacky approach to creating a background program. I suggest looking at [How to create a Service](https://learn.microsoft.com/en-us/dotnet/framework/windows-services/walkthrough-creating-a-windows-service-application-in-the-component-designer) for the "proper" way of achieving what you want. As for executing code right before it exits, check out [this](https://stackoverflow.com/a/2555354/7662085) answer. – stelioslogothetis Aug 30 '17 at 12:48
  • @stybl I tried creating Service project but because of some technical limitations, it had to be abandoned. But you are right that would be the cleanest solution. – user6879072 Aug 30 '17 at 12:52
  • @BhubanShrestha I'm not sure that you can run a console app in the background without UI. I have a big code base that I would have to check for compatibility. It is best if I keep the winforms approach. – user6879072 Aug 30 '17 at 12:55
  • @Vikhram I am open to other solutions as well because you are right, that might be the problem. – user6879072 Aug 30 '17 at 12:56
  • Possible duplicate of [Capture console exit C#](https://stackoverflow.com/questions/474679/capture-console-exit-c-sharp) – stop-cran Aug 30 '17 at 13:01
  • @stop-cran this is not a duplicate my app is winforms not a console app and thus the solution in that post doesn't work for me. – user6879072 Aug 30 '17 at 13:06
  • What "technical limitations" did you run into creating a Windows Service? – mason Aug 30 '17 at 13:09
  • @mason If I remember correctly it was something with listening to mouse events. Regardless the app is already deployed this would be an update for it. – user6879072 Aug 30 '17 at 13:12

1 Answers1

1

Let me break down your situation: An excerpt from learn.microsoft.com on SetConsoleCtrlHandler

Each console process has its own list of application-defined HandlerRoutine functions that handle CTRL+C and CTRL+BREAK signals. The handler functions also handle signals generated by the system when the user closes the console, logs off, or shuts down the system. A console process adds or removes additional handler functions by calling the SetConsoleCtrlHandler function, which does not affect the list of handler functions for other processes.

As you notice, this seems to be referring to Console process only. And it cannot tell you when other application exit, but only about this application. In addition it can notify you of LOGOFF and SHUTDOWN. Also, since there is no console (as you want a background process), there is no CTRL-C or CTRL-BREAK processing involved.
Conclusion: the best case is that you can only be notified about LOGOFF and SHUTDOWN

Relevant information from the same page for winforms app:

This function provides a similar notification for console application and services that WM_QUERYENDSESSION provides for graphical applications with a message pump.

 

What is the problem with your code?

Well, it is designed to provide a callback when the system has LOGOFF or SHUTDOWN when the application is running. Needless to say, your application has already exited to receive such events. So you have to wait after registering your callback.

 

What is the best way to do it?

You should write a service for this. And if I am not mistaken, service already gets a notification for LOGOFF and SHUTDOWN How to write a simple C# service has a good example.

EDIT:

For more comprehensive understanding , you might want to look at the MSDN docs about ServiceBase class. Also, you should look at the OnSessionChange event, which is notified about all session events. It has some useful code excerpt at SessionChangeDescription class doc. Similarly for OnShutdown event

Now if you can't do windows service, you will have to create a Form from your winform application and make the Form invisible. You should then handle the WM_QUERYENDSESSION event or use the SystemEvents.SessionEnding Event to handle the situation

Sample code for that

var frm = new Form();
frm.Activated += (s, e) => { frm.Visible = false; };
SystemEvents.SessionEnded += (s, e) => { /* your code to handle logoff and shutdown */ };
Application.Run(frm);
Vikhram
  • 4,294
  • 1
  • 20
  • 32