我正在使用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。
您可以使用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;
}
}
}
This answer here有关于通过Powershell检查Windows注册表以查看是否已安排重新启动的信息:
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
除此之外,ExitWindowsEx
有一个可笑的长超时(最大是315360000
- 10年),然后检查ERROR_SHUTDOWN_IS_SCHEDULED
- 如果命令成功,那么你会想(当然)AbortSystemShutdown
...