Как определить, закрывается ли Windows или перезагружается - PullRequest
15 голосов
/ 11 июня 2009

Я знаю, что когда Windows закрывается, она отправляет сообщение WM_QUERYENDSESSION каждому приложению. Это позволяет легко определить, когда Windows завершает работу. Однако можно ли узнать, будет ли компьютер выключен или он перезагрузится после завершения работы Windows.

Я не особо полон надежд, учитывая, что в документации MSDN есть что-то о WM_QUERYENDSESSION: «... невозможно определить, какое событие происходит», но накопленная ловкость stackoverflow никогда не перестает меня удивлять .

Ответы [ 4 ]

8 голосов
/ 25 декабря 2013

В Windows 7 (и, вероятно, также в Vista / 8 / Server) вы можете использовать системные события, чтобы отслеживать, выключается ли Windows (и выключается ли компьютер) или просто перезагружается. Каждый раз, когда инициируется выключение / перезагрузка (любым способом - нажатием кнопки в меню «Пуск» или программным способом), Windows 7 записывает одно или два события в системный журнал, источник USER32, идентификатор события 1074. Эти события можно записать, если Вы открываете Event Viewer из Администрирования (отфильтруйте системный журнал, чтобы видеть только ID 1074). Описание (сообщение) этих событий содержит тип отключения. Таким образом, вы можете проанализировать описание самого последнего события этого типа (после завершения выключения), ища необходимое слово (завершение работы, перезагрузка / перезапуск).

Я не пытался увидеть тип выключения, записанный в событии, при использовании кнопки питания для корректного выключения Windows (я обычно отключаю эту функцию), но на некоторых сайтах предлагается указывать тип «выключения» вместо « отключение "- так что проверьте, если вам нужно быть уверенным. Или просто ищите тип «перезагрузка» - если он не найден, то предполагается тип «выключение».

В Windows XP, по моему опыту, событие 1074 записывается только в том случае, если завершение работы / перезагрузка выполняется программно (например, во время установки программы или с помощью утилиты shutdown.exe). Таким образом, он не регистрирует выключения, инициированные из оболочки (проводника), но, возможно, вы могли бы объединить этот метод с чтением значения из реестра, как предложено в другом ответе. Кроме того, имейте в виду, что в WinXP сообщение о событии 1074 содержит слово «перезапуск», независимо от того, каков реальный тип выключения, поэтому вам следует посмотреть в поле «Тип выключения:», в котором будет указано «выключение» или "перезагрузка".

В связи с этим идентификатор события 1073 записывается всякий раз, когда Windows по какой-либо причине не удается завершить работу / перезагрузить компьютер (например, если приложение не разрешает отключение в ответ на WM_QUERYENDSESSION). В этом случае сообщение также будет содержать слова «завершение работы», «перезагрузка» или «выключение» - в WinXP. Для Win7 этот тип события менее полезен в нашем случае, так как он не будет иметь никакого значения между выключением и перезагрузкой. Но для WinXP - если вам нужно только перехватить выключение / перезагрузку, выполнить некоторые действия, а затем продолжить соответствующий процесс выключения или перезагрузки - он должен работать как положено.

6 голосов
/ 11 июня 2009

С здесь :

Вы можете прочитать значение DWORD из «HKCU \ Software \ Microsoft \ Windows \ CurrentVersion \ Explorer \ Shutdown Настройка ", чтобы определить, что пользователь последний выбранный из выключения диалоговое окно.

Немного обходного решения, но оно должно сработать.

5 голосов
/ 07 января 2011

Уловка, которая обычно работает, состоит в том, чтобы отловить WM_ENDSESSION и зарегистрировать его. Теперь следите за временем. Если система возвращается в разумные сроки (скажем, 5 минут). Тогда это была перезагрузка, а не выключение.

Идея : Если система возвращается в течение 5 минут, действительно ли это действительно имеет значение, если пользователь нажал «shutdown» или «reboot»?

Если вам действительно необходимо обнаружить завершение работы (и единственная причина, по которой я думаю, что вам нужно это сделать, - это если вы зависите от непонятного поведенческого различия между выключением и перезагрузкой), вы можете исследовать API hooking из ExitWindowsEx и связанных функций, но я не рекомендую этот подход. Подумайте, действительно ли вам нужно обнаружить это напрямую.

2 голосов
/ 16 декабря 2015

Возможное экспериментальное решение для Windows7 может быть следующим. (Я не уверен, что это хорошо работает с другими локализациями, поэтому я бы назвал это обходным путем)

using System.Diagnostics.Eventing.Reader;

namespace MyApp
{
public class RestartDetector : IDisposable
{
    public delegate void OnShutdownRequsted(bool restart);
    public OnShutdownRequsted onShutdownRequsted;

    private EventLogWatcher watcher = null;

    public RestartDetector()
    {
        try
        {
            EventLogQuery subscriptionQuery = new EventLogQuery(
                "System", PathType.LogName, "*[System[Provider[@Name='USER32'] and (EventID=1074)]]");

            watcher = new EventLogWatcher(subscriptionQuery);

            // Make the watcher listen to the EventRecordWritten
            // events.  When this event happens, the callback method
            // (EventLogEventRead) is called.
            watcher.EventRecordWritten +=
                new EventHandler<EventRecordWrittenEventArgs>(
                    EventLogEventRead);

            // Activate the subscription
            watcher.Enabled = true;
        }
        catch (EventLogReadingException e)
        {
        }
    }

    public void EventLogEventRead(object obj, EventRecordWrittenEventArgs arg)
    {
        bool restart = false;
        try
        {
            // Make sure there was no error reading the event.
            if (arg.EventRecord != null)
            {
                String[] xPathRefs = new String[1];
                xPathRefs[0] = "Event/EventData/Data";
                IEnumerable<String> xPathEnum = xPathRefs;

                EventLogPropertySelector logPropertyContext = new EventLogPropertySelector(xPathEnum);
                IList<object> logEventProps = ((EventLogRecord)arg.EventRecord).GetPropertyValues(logPropertyContext);

                string[] eventData = (string[])logEventProps[0];

                foreach (string attribute in eventData)
                {
                    if (attribute.Contains("restart")) { restart = true; break; }
                }
            }
        }
        catch (Exception e)
        {
        }
        finally
        {
            if (onShutdownRequsted != null) { onShutdownRequsted(restart); }
        }   
    }

    public void Dispose()
    {
        // Stop listening to events
        if (watcher != null)
        {
            watcher.Enabled = false;
            watcher.Dispose();
        }
    }
}
}

Ниже приведен пример XML, который записывается в журнал событий при перезагрузке компьютера:

- <Event xmlns="http://schemas.microsoft.com/win/2004/08/events/event">
- <System>
  <Provider Name="USER32" /> 
  <EventID Qualifiers="32768">1074</EventID> 
  <Level>4</Level> 
  <Task>0</Task> 
  <Keywords>0x80000000000000</Keywords> 
  <TimeCreated SystemTime="2015-12-15T11:10:43.000000000Z" /> 
  <EventRecordID>90416</EventRecordID> 
  <Channel>System</Channel> 
  <Computer>WIN7PC</Computer> 
  <Security UserID="S-1-5-21-1257383181-1549154685-2724014583-1000" /> 
  </System>
- <EventData>
  <Data>C:\Windows\system32\winlogon.exe (WIN7PC)</Data> 
  <Data>WIN7PC</Data> 
  <Data>No title for this reason could be found</Data> 
  <Data>0x500ff</Data> 
  <Data>restart</Data> 
  <Data /> 
  <Data>WIN7PC\WIN7PCUser</Data> 
 <Binary>FF00050000000000000000000000000000000000000000000000000000000000</Binary> 
  </EventData>
  </Event>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...