如何在调用InitiateSystemShutdown后检测将来是否安排重新启动?

问题描述 投票:1回答:2

我正在使用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),但我需要这样做而不再调用reboot(因为我可能不想在此时实际触发重启) 。

我试过用参数GetSystemMetrics调用SM_SHUTTINGDOWN,但即使我知道这是预定的,这将返回false / 0。我假设这是一个未计划发生的实际停机事件。

无论如何,我可以检测到此呼叫正在进行中,或者更一般地是否已安排重新启动?

我使用C#/ DllImport / interop调用这些方法,所以我需要一个可以从c#进程访问的api。

c# windows interop pinvoke
2个回答
1
投票

您可以使用Windows Event Log API访问“系统”通道上的日志条目。该日志通过登录记录请求以关闭系统,并中止系统关闭。

此代码显示如何使用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;
    }
  }
}

-2
投票

This answer here有关于通过Powershell检查Windows注册表以查看是否已安排重新启动的信息:

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

除此之外,ExitWindowsEx有一个可笑的长超时(最大是315360000 - 10年),然后检查ERROR_SHUTDOWN_IS_SCHEDULED - 如果命令成功,那么你会想(当然)AbortSystemShutdown ...

© www.soinside.com 2019 - 2024. All rights reserved.