Как я могу определить, запланирована ли перезагрузка в будущем после вызова InitiateSystemShutdownW?

В настоящее время я запускаю перезагрузку, используя вызов Windows API InitiateSystemShutdownW. Я передаю это значение dwTimeout (10 минут). Я хочу определить, запланирована ли эта перезагрузка (или любая другая перезагрузка):

[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool InitiateSystemShutdownW([MarshalAs(UnmanagedType.LPWStr)]String lpMachineName, [MarshalAs(UnmanagedType.LPWStr)]String lpMessage, int dwTimeout, bool bForceAppsClosed, bool bRebootAfterShutdown);

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

Я пытался вызвать GetSystemMetrics с параметром SM_SHUTTINGDOWN, но хотя я знаю, что это запланировано, возвращается false/0. Я предполагаю, что это фактическое отключение, которое не запланировано.

Могу ли я в любом случае определить, что этот вызов выполняется, или, в более общем плане, запланирована ли перезагрузка или нет?

Я вызываю эти методы с помощью C#/DllImport/interop, поэтому мне нужен API, доступный из процесса C#.

Внутри winlogon.exe есть переменные, записывающие ожидаемое время выключения, сообщение, флаги и т. д., но нет общедоступного API для извлечения этой информации из него (кроме использования журнала событий). В Windows действительно должно быть что-то вроде API GetPendingShutdownDetails. Я создал задачу центра обратной связи, попросив Microsoft добавить ее — aka.ms/AAcdp86

Simon Kissane 02.05.2021 06:04
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
1
535
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Этот ответ здесь содержит информацию о проверке реестра Windows через Powershell, чтобы узнать, запланирована ли перезагрузка:

Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'

Кроме того, ExitWindowsEx со смехотворно долгим тайм-аутом (максимум 315360000 - 10 лет), затем проверка на ERROR_SHUTDOWN_IS_SCHEDULED - если команда выполнена успешно, то вам захочется (конечно) AbortSystemShutdown...

В этом ответе говорится, что это не вернет true, если перезагрузка запланирована с помощью shutdown.exe, она вернет true, только если Windows скажет, что ваш компьютер необходимо перезагрузить. shutdown.exe — это просто оболочка для InitiateSystemShutdownW. Я проверяю, но я думаю, что это неправильно. Я также указал в вопросе, что не хочу запускать перезагрузку (долго или нет)

Liam 25.04.2019 17:32

Приносим свои извинения и сожаления по поводу отрицательных голосов: \ Но один из ваших вопросов гласит: «Могу ли я каким-либо образом определить, что этот вызов выполняется или, в более общем случае, запланирована ли перезагрузка или нет

Frank Alvaro 25.04.2019 17:42

Но это не обнаруживает «общие» запланированные перезагрузки, хотя делает это, иначе это сработало бы для меня, общего типа, неспецифического. Он обнаруживает этот конкретный сценарий. Это не то, что означает общее. Если вы думаете, что это дублировать, то вы также не должны добавлять ответ

Liam 26.04.2019 10:12

У метода «смехотворно долгого тайм-аута» есть недостаток, заключающийся в том, что он загрязняет журнал событий.

Simon Kissane 02.05.2021 06:31
Ответ принят как подходящий

Вы можете использовать API журнала событий Windows для доступа к записям журнала на канале «Система». В этом журнале записываются запросы входа в систему на выключение системы и прерывание выключения системы.

В этом коде показано, как использовать P/Invoke для получения количества запросов на завершение работы системы и отмену завершения работы в течение заданного периода времени перед запросом (в миллисекундах). Таким образом, если у вас есть 2 запланированных запроса на отключение за последний час и 1 запрос на отмену, то у вас есть отложенное завершение работы.

Если вам действительно нужны все подробности о зарегистрированном запросе на отключение, вы можете использовать функцию EvtRender для извлечения данных в формате XML и их анализа. Этот код этого не делает.

using System;
using System.Runtime.InteropServices;

namespace WindowsEventLogChecker
{
  // partial list from "winerror.h"
  public enum ERROR
  {
    ERROR_SUCCESS,
    ERROR_NO_MORE_ITEMS = 259,
    ERROR_EVT_CHANNEL_NOT_FOUND = 15007,
    ERROR_EVT_INVALID_QUERY = 15001
  }

  // this is our own enum
  public enum SystemEventType
  {
    Shutdown,
    Abort
  }

  // these are from "winevt.h"
  public enum EVT_QUERY_FLAGS
  {
    EvtQueryChannelPath = 0x1,
    EvtQueryFilePath = 0x2,
    EvtQueryForwardDirection = 0x100,
    EvtQueryReverseDirection = 0x200,
    EvtQueryTolerateQueryErrors = 0x1000
  }


  class Program
  {
    [DllImport("wevtapi.dll", EntryPoint = "EvtQuery", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    public static extern IntPtr EvtQuery(IntPtr session, [MarshalAs(UnmanagedType.LPWStr)] string path, [MarshalAs(UnmanagedType.LPWStr)] string query, int flags);

    [DllImport("wevtapi.dll", EntryPoint = "EvtNext", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
    public static extern bool EvtNext(IntPtr resultSet, int batchSize, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] eventBatch, int timeout, int flags, ref int nReturned);

    [DllImport("wevtapi.dll", EntryPoint = "EvtClose", CallingConvention = CallingConvention.StdCall)]
    public static extern bool EvtClose(IntPtr handle);

    [DllImport("kernel32.dll", EntryPoint = "GetLastError", CallingConvention = CallingConvention.StdCall)]
    public static extern int GetLastError();

    static void Main(string[] args)
    {
      // get the number of scheduled shutdowns in the last hour
      int nShutdowns = GetEventCount(SystemEventType.Shutdown, 3600000);

      // get the number of aborted shutdowns in the last hour
      int nAborts = GetEventCount(SystemEventType.Abort, 3600000);
    }

    private static int GetEventCount(SystemEventType evtType, int timeSpanMs)
    {
      ERROR status = ERROR.ERROR_SUCCESS;
      IntPtr hResult = IntPtr.Zero;
      IntPtr[] eventBatch = new IntPtr[10];

      // these 2 event id's, along with 'USER32' event source, denote requested
      // shutdown and abort, respectively
      string shutDownId = "1074";
      string abortId = "1075";

      // XPath query to get the event id, event source, and timespan in ms from now 
      // back to when the event was posted to the event log.
      string format = "*[System[(EventID = {0}) and Provider[@Name=\"USER32\"] and TimeCreated[timediff(@SystemTime) <= {1}]]]";

      // The "System" event channel
      string channelPath = "System";

      int nEvents = 0;
      int count = 0;
      string evtQuery;

      switch (evtType)
      {
        case SystemEventType.Shutdown:
        evtQuery = string.Format(format, shutDownId, timeSpanMs);
        break;
        case SystemEventType.Abort:
        evtQuery = string.Format(format, abortId, timeSpanMs);
        break;
        default:
        throw new InvalidOperationException();
      }
      hResult = EvtQuery(IntPtr.Zero, channelPath, evtQuery, (int)(EVT_QUERY_FLAGS.EvtQueryChannelPath | EVT_QUERY_FLAGS.EvtQueryReverseDirection));
      if (IntPtr.Zero == hResult)
      {
        status = (ERROR)GetLastError();

        if (status == ERROR.ERROR_EVT_CHANNEL_NOT_FOUND)
        {
          // log error
          return 0;
        }
        else if (status == ERROR.ERROR_EVT_INVALID_QUERY)
        {
          // log error
          return 0;
        }
        else
        {
          // log error
          return 0;
        }
      }
      while (EvtNext(hResult, 10, eventBatch, 1000, 0, ref nEvents))
      {
        for (int i = 0; i < nEvents; i++)
        {
          count++;
          if (eventBatch[i] != IntPtr.Zero)
          {
            EvtClose(eventBatch[i]);
          }
        }
      }
      status = (ERROR)GetLastError();
      if (status != ERROR.ERROR_NO_MORE_ITEMS)
      {
        // log error here and cleanup any remaining events
        for (int i = 0; i < nEvents; i++)
        {
          if (eventBatch[i] != IntPtr.Zero)
          {
            EvtClose(eventBatch[i]);
          }
        }
      }
      if (hResult != null)
      {
        EvtClose(hResult);
      }
      return count;
    }
  }
}

Кажется, моя рабочая нагрузка была «перераспределена», поэтому мне придется проверить это в другой раз. Хотя, похоже, разумное решение, спасибо.

Liam 26.04.2019 10:10

Теоретически тайм-аут выключения может составлять до 10 лет — максимальный тайм-аут составляет 3650 дней минус одна секунда — и поэтому этот метод может не найти сообщение о завершении работы в журнале событий, потому что он не оглядывается назад достаточно далеко (как представлено). он оглядывается только на 1 час назад), или даже событие выключения было так давно, что было удалено из журнала. (На практике эта проблема маловероятна, потому что вероятность отключения на более короткие периоды времени, например, на 1 час или 5 минут, гораздо выше.)

Simon Kissane 02.05.2021 06:38

Почему бы вам вместо этого не использовать класс Eventlog()?

marsh-wiggle 24.05.2021 19:03

Другие вопросы по теме