Search code examples
c#eventsshutdown

System Events (SessionEnding) not working as expected


I have made a simple windows application (without any Form though) and in it I used SessionEnding to do something before the OS shutsdown according to recipe19.1 in C# Cookbook (that I modified since Debug will not work anyway)

The application creates a text file when started (it does) and then when any event takes places should create another blank text file. The code is below and it does not work as expected. Nothing is created when an event takes place. Can someone tell me what is wrong with this?


As a sidenote, I tried other examples this time using an override of WndProc as stated in the documentation and it seems to be working but I am not sure how to apply this to an application without forms


The code

using Microsoft.Win32;
using System;
using System.Diagnostics;
using System.Threading;

namespace NoConsoleApp2
{
    static class Program
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {    
            RegisterForSystemEvents();
            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Onstart.txt");

            bool keepRunning = true;
            Debug.WriteLine("Start");
            for (int i = 0; i < 100 && keepRunning; i++)
            {
                Thread.Sleep(1000);
            }    
            Debug.WriteLine("End");
        }

        public static void RegisterForSystemEvents()
        {
            //Always get the final notification when the event thread is shutting down 
            //so we can unregister.

            SystemEvents.EventsThreadShutdown += new EventHandler(OnEventsThreadShutdown);
            SystemEvents.PowerModeChanged += new PowerModeChangedEventHandler(OnPowerModeChanged);
            SystemEvents.SessionSwitch += new SessionSwitchEventHandler(OnSessionSwitch);
            SystemEvents.SessionEnding += new SessionEndingEventHandler(OnSessionEnding);
            SystemEvents.SessionEnded += new SessionEndedEventHandler(OnSessionEnded);
        }
        private static void UnregisterFromSystemEvents()
        {
            SystemEvents.EventsThreadShutdown -= new EventHandler(OnEventsThreadShutdown);
            SystemEvents.PowerModeChanged -= new PowerModeChangedEventHandler(OnPowerModeChanged);
            SystemEvents.SessionSwitch -= new SessionSwitchEventHandler(OnSessionSwitch);
            SystemEvents.SessionEnding -= new SessionEndingEventHandler(OnSessionEnding);
            SystemEvents.SessionEnded -= new SessionEndedEventHandler(OnSessionEnded);

        }
        /* Notifies you when the thread that is distributing the events from the SystemEvents class is
         * shutting down so that we can unregister events on the SystemEvents class
         */ 
        private static void OnEventsThreadShutdown(object sender, EventArgs e)
        {
            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "END.txt");
            Debug.WriteLine("System event thread is shutting down, no more notifications");
            //Unregister all our events as the notification thread is goin away
            UnregisterFromSystemEvents();
        }
        /* Triggers when the user suspends or resumes the system from a suspended state
         */ 
        private static void OnPowerModeChanged(object sender, PowerModeChangedEventArgs e)
        {
            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "power.txt");
            switch (e.Mode)
            {
                case PowerModes.Resume:
                    Debug.WriteLine("PowerMode: OS is resuming from suspended state");
                    break;
                case PowerModes.StatusChange:
                    Debug.WriteLine("PowerMode: There was a change relating to the power supply (weak battery, unplug, etc...)");
                    break;
                case PowerModes.Suspend:
                    Debug.WriteLine("PowerMode: OS is about to be suspended");
                    break;

            }
        }
        /* Triggered by a change in the logged-on user
         */ 
        private static void OnSessionSwitch(object sender, SessionSwitchEventArgs e)
        {
            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "sessionswitch.txt");

            switch (e.Reason)
            {
                case SessionSwitchReason.ConsoleConnect:
                    Debug.WriteLine("Session connected from the console");
                    break;
                case SessionSwitchReason.ConsoleDisconnect:
                    Debug.WriteLine("Session disconnected from the console");
                    break;
                case SessionSwitchReason.RemoteConnect:
                    Debug.WriteLine("Remote Session connected");
                    break;
                case SessionSwitchReason.RemoteDisconnect:
                    Debug.WriteLine("Remote Session disconnected");
                    break;
                case SessionSwitchReason.SessionLock:
                    Debug.WriteLine("Session has been locked");
                    break;
                case SessionSwitchReason.SessionLogoff:
                    Debug.WriteLine("User was logged off from a session");
                    break;
                case SessionSwitchReason.SessionLogon:
                    Debug.WriteLine("User has logged on to a session");
                    break;
                case SessionSwitchReason.SessionRemoteControl:
                    Debug.WriteLine("Session changed to or from remote status");
                    break;
                case SessionSwitchReason.SessionUnlock:
                    Debug.WriteLine("Session has been unlocked");
                    break;

            }
        }
        /* Triggered when the user is trying to log off or shut down the system
         */ 
       private static void OnSessionEnding(object sender, SessionEndingEventArgs e)
        {
            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "sessionend.txt");

            //True to cancel the user request to end the session, false otherwise
            e.Cancel =  false;

            switch(e.Reason)
            {
                case SessionEndReasons.Logoff:
                    Debug.WriteLine("Session ending as the user is logging off");
                    System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Logoff.txt");
                    break;
                case SessionEndReasons.SystemShutdown:
                    Debug.WriteLine("Session ending as the OS is shutting down");
                    System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Shutdown.txt");
                    break;

            }

        }
        /*  Triggered when the user is actually logging off or shutting down the system
         */ 
        private static void OnSessionEnded(object sender, SessionEndedEventArgs e )
        { 
            switch(e.Reason)
            {
                case SessionEndReasons.Logoff:
                    Debug.WriteLine("Session ended as the user is logging off");
                    System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Loggedoff.txt");
                    break;
                case SessionEndReasons.SystemShutdown:
                    Debug.WriteLine("Session ended as the OS is shutting down");
                    System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Shutted.txt");
                    break;
            }

        }


    }
}

EDIT:

I have been advised that I should use Application.Run and with this the events are responding. However I assume Run makes a loop so I am wondering where to put my own logic (in this example case the for loop)

static void Main()
{    
    RegisterForSystemEvents();
    System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Onstart.txt");

    bool keepRunning = true;
    Debug.WriteLine("Start");

    Application.Run();  //this activates the events

    System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "GoFOrloop.txt");

    for (int i = 0; i < 100 && keepRunning; i++)
    {
        Thread.Sleep(1000);
    }    
    Debug.WriteLine("End");
}

In this case, the file GoForLoop.txt is never created (meaning that the loop itself is never executed)

EDIT2: I have tried the solution in EDIT and it works (the events are triggered) but the main logic(in this case the loop) does not work so I tried a second suggestion to use Application Contexts. Sadly this time eventhough the loop works, the events are not triggered anymore :(

class Context : ApplicationContext
    {

        public Context()
        {
            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "GoFOrLoop.txt");
            for (int i = 0; i < 60; i++)
            {
                //    Console.WriteLine("Number: " + i);
                Thread.Sleep(1000);
            }



            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "end.txt");
            Debug.WriteLine("End");


            //  Environment.Exit(1);
            ExitThread();
        }



    }

and in the Main

 [STAThread]
        static void Main()
        {
            /*    Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new Form1());
                */

            RegisterForSystemEvents();

            System.IO.File.Create(AppDomain.CurrentDomain.BaseDirectory + "Onstart.txt");

//            bool keepRunning = true;

            Debug.WriteLine("Start");

             //           Application.Run(); //this works
            Application.Run(new Context()); //this DOES NOT work
}

Solution

  • Windows apps for process events uses message loop. In your code you don't run message loop.

    Add to Main method:

    Application.Run();
    

    This starts meessage loop for your app.