Как отменить событие выключения ПК?
Я делаю приложение, в котором я хочу выполнить некоторые запросы к базе данных непосредственно перед выключением системы. Я использую этот код -
static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
e.Cancel = true;
MessageBox.Show("Shut down canceled");
}
Я выполнил это приложение и попытался завершить работу системы, и этот код также зафиксировал событие завершения работы, но проблема заключается в том, что после отображения окна сообщения также отображается этот экран - [Я не могу опубликовать изображение, так как у меня нет 10 точки.]
он показывает имя моего приложения, которое остановило выключение системы, и также предоставляет "кнопку принудительного выключения", я не хочу, чтобы отображался этот экран, поскольку пользователь может принудительно завершить работу системы до завершения выполнения моих запросов.
Нужны экспертные советы по этому, заранее большое спасибо.
2 ответа
Краткий надежный ответ:
В любой последней версии Windows вы можете попытаться отменить завершение работы, но Windows может решить игнорировать вас; это печально по замыслу. Если вам абсолютно необходимо завершить операции до того, как ваш процесс завершится, правильное место для этого - обработчик SessionEnded. Если у вас есть задачи, которые должны быть выполнены до завершения вашего процесса, вы не должны возвращаться из обработчика SessionEnded до тех пор, пока вся ваша работа не будет завершена (поэтому ваши запросы и т. Д. Завершены).
Поэтому вместо (или, если хотите, обработки) SessionEnding, обработайте SessionEnded и работаете ли вы там:
static void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e)
{
WaitForQueriesToFinishOrSaveState(); // new code
}
Как вы реализуете это ожидание, будет зависеть от вашего приложения; если вам нужно выполнить запросы заново, вы можете выполнить их там, или вам может понадобиться Thread.Join () или иным образом дождаться завершения фоновых задач; но это должно быть блокирующее ожидание (поэтому вам не нужно возвращаться из функции, пока вы не закончите).
Поскольку вы не можете полностью остановить выключение, в этом случае, возможно, нет смысла пытаться отменить отмену, поэтому я бы рекомендовал вообще не устанавливать e.Cancel в SessionEnding. В старых версиях Windows это было более значимым, а теперь, к сожалению, гораздо менее значимым.
Документам API также рекомендуетсяне выполнять какую-либо значительную работу в SessionEnding (включая окна сообщений), но для установки любых флагов необходимо немедленно вернуться, а затем выполнить работу в SessionEnded. (Не доказано: я подозреваю, что если вы не вернетесь достаточно быстро, это может ускорить появление у пользователя экрана "программы ведут себя плохо, хотите ли вы их убить", поскольку Windows считает, что вы не играете приятно больше.)
За кулисами:
Настройка e.Cancel указывает Windows, что вы хотите, чтобы сессия не заканчивалась; но пользователь все еще получает взгляд; и Windows может принять решение проигнорировать ваш запрос по любой причине, которая ему кажется уместной. Вот так печенье крошится. Вы можете найти хаки, которые работают ситуативно, но не существует API или подходов, которые одобрены Microsoft и, следовательно, вероятно, будут работать последовательно сейчас и в будущем.
Под прикрытием Windows отправляет окнам вашего процесса сообщение WM_QUERYENDSESSION, которое.NET получает для вас и преобразует в событие SessionEnding) и передает вашу отмену (или ее отсутствие) обратно в Windows; возвращает TRUE, если вы не отменяете, FALSE, если вы это делаете.
После этого Windows просматривает все запросы процессов и в зависимости от причины завершения работы и других факторов может по-прежнему принимать решение, несмотря на такие запросы. Он также может предупредить пользователя, если процессы не взаимодействуют, и дать ему возможность убить процесс.
Независимо от того, обрабатываете ли вы WM_QUERYENDSESSION (SessionEnding) или нет, у вас всегда есть последний шанс на очистку: вам отправляется сообщение WM_ENDSESSION (переведенное в SessionEnded). Сложность в том, что вы должны выполнить все свои жизненно важные задачи, прежде чем вернутся все ваши обработчики SessionEnded!
Как только Windows возвращается к своему вызову WM_ENDSESSION (SessionEnded), все ставки отключаются, если речь идет о времени жизни вашего приложения, и Windows может завершить ваш процесс в любой момент.
Раймонд Чен освещал это довольно искусно и совсем недавно.
http://blogs.msdn.com/b/oldnewthing/archive/2013/06/27/10429232.aspx
В качестве сноски SystemEvents.SessionEnded - это удобство; если у вас есть окно приложения верхнего уровня, вы можете полностью его обойти и добиться того же с помощью:
protected override void WndProc(ref Message m)
{
if (m.Msg == 0x16) // WM_ENDSESSION
{
WaitForQueriesToFinishOrSaveState();
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
В команде выключения есть переключатель для прерывания работы. Вы должны вызвать эту команду с помощью вашего кода C#
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());