Как определить, выключается ли Windows или перезапускается

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

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

Ответы

Ответ 1

Из здесь:

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

Немного кругового решения, но оно должно сделать трюк.

Ответ 2

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

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

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

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

Ответ 3

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

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

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

Ответ 4

Возможное экспериментальное решение для 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>