繁体   English   中英

调用InitiateSystemShutdownW之后,如何确定将来是否计划重新启动?

[英]How can I detect if a reboot is scheduled in the future after calling InitiateSystemShutdownW?

我目前正在使用Windows API调用InitiateSystemShutdownW触发重新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 ),但是我需要执行此操作而无需再次调用重新启动(因为此时我可能不想实际触发重新启动) 。

我尝试使用参数SM_SHUTTINGDOWN调用GetSystemMetrics ,但是即使我知道这已计划,它也会返回false / 0 我以为这是一次实际的关机,没有按计划进行。

无论如何,无论是否计划重新启动,我是否都能检测到该呼叫正在进行中?

我正在使用C#/ DllImport / interop调用这些方法,因此我需要可以从ac#进程访问的api。

您可以使用Windows事件日志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;
    }
  }
}

这里的答案有关于通过Powershell检查Windows注册表以查看是否计划重新启动的信息:

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

除此之外, ExitWindowsEx超时时间非常长(最大为315360000 -10年),然后检查ERROR_SHUTDOWN_IS_SCHEDULED如果命令成功,那么您将(当然) AbortSystemShutdown ...

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM