3

I am making an application in which i want to execute some database queries just before system get shut down. I am using this code -

static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
        e.Cancel = true;
        MessageBox.Show("Shut down canceled");
}

I did execute this application and tried to shut down the system and this code captured the shut down event also but the problem is after showing message box it shows this screen also- [I can't post the image as i don't have 10 points.]

it shows the name of my application that stopped the system to get shut down and it also provide "Force Shut down button" , i dont want this screen to be displayed as user can forcefully shut down the system before completion of execution of my queries.

Need expert advices on this, thanks a lot in advance.

2 Answers2

2

The Short Reliable Answer:

On any recent Windows version, you can try to cancel shutdown but Windows may decide to ignore you; this is sadly by design. If you absolutely have to finish operations before your process is terminated, the right place to do this is in the SessionEnded handler. If you have tasks that must complete before your process terminates, you must not return from your SessionEnded handler until all your work is done (so your queries, etc. have finished.)

So instead of (or as well as, if you prefer) handling SessionEnding, handle SessionEnded and do you work there:

static void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
    WaitForQueriesToFinishOrSaveState(); // new code
}

How you implement that waiting will depend on your application; if you need to run queries afresh you may be able to do them therein, or you may need to Thread.Join() or otherwise wait for background tasks to complete; but it must be a blocking wait (so you need to not return from the function until you're done).

Since you can't absolutely stop shutdown, there's perhaps little point in attempting the cancellation in this case, so I'd recommend not setting e.Cancel in SessionEnding at all. On older Windows versions this was more meaningful, very much less so now unfortunately.

It's also recommended by the API docs not to do any significant work in SessionEnding (including message boxes), but to set any flags you need to return immediately and then do the work in SessionEnded. (Unproven aside: I'm suspicious that if you don't return quickly enough, this may hasten the appearance of the "programs are misbehaving, do you want to kill them" screen for the user, as Windows believes you're not playing nice any more.)

Behind the Scenes:

Setting e.Cancel indicates to Windows that you'd like the session not to end; but the user still gets a look in; and Windows may decide to ignore your request for any reason it feels pertinent. That's just the way the cookie crumbles. You may find hacks that work situationally, but there's no API or approach which is Microsoft-approved and therefore likely to work consistently now and in the future.

Under the covers, Windows is sending your process' windows a WM_QUERYENDSESSION message, which .NET receives for you and translates into the SessionEnding event) and will pass your cancellation (or lack of) back to Windows; returning TRUE if you don't cancel, FALSE if you do.

After this, Windows takes a look at all process' requests and depending on the cause of the shutdown and other factors may well still decide to go ahead despite such requests. It may also alert the user if processes are not cooperating and give them the option of killing the process.

Whether you handle WM_QUERYENDSESSION (SessionEnding) or not, you always get one last chance to clean up: you're sent a WM_ENDSESSION message (translated into SessionEnded). The tricky part is that you have to do all your vital tasks before all your SessionEnded handlers have returned!

Once Windows hears back from its WM_ENDSESSION (SessionEnded) call, all bets are off as far as your application's lifetime is concerned and Windows can terminate your process at any point.

Raymond Chen covered this quite expertly and quite recently.

http://blogs.msdn.com/b/oldnewthing/archive/2013/06/27/10429232.aspx

As a footnote, SystemEvents.SessionEnded is a convenience; if yo have a top level application window you can bypass it entirely and achieve the same via:

protected override void WndProc(ref Message m)
{
    if (m.Msg == 0x16) // WM_ENDSESSION
    {
        WaitForQueriesToFinishOrSaveState();
        m.Result = IntPtr.Zero;
        return;
    }
    base.WndProc(ref m);
}
El Zorko
  • 3,349
  • 2
  • 26
  • 34
0

in shutdown command there's a switch for abort shutdown. you have to call this command by your c# code

        Process cmd = new Process();
        cmd.StartInfo.FileName = "cmd.exe";
        cmd.StartInfo.RedirectStandardInput = true;
        cmd.StartInfo.RedirectStandardOutput = true;
        cmd.StartInfo.CreateNoWindow = false;
        cmd.StartInfo.UseShellExecute = false;

        cmd.Start();



        cmd.StandardInput.WriteLine(@"shutdown -a");
        cmd.StandardInput.Flush();
        cmd.StandardInput.Close();
        Console.WriteLine(cmd.StandardOutput.ReadToEnd());
Mostafa Soghandi
  • 1,524
  • 1
  • 12
  • 20