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

В данный момент я запускаю перезагрузку с помощью вызова API Windows 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 #.

Ответы [ 2 ]

1 голос
/ 25 апреля 2019

Вы можете использовать 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;
    }
  }
}
0 голосов
/ 25 апреля 2019

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

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

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

...