在调用 InitiateSystemShutdownW 之后,如何检测将来是否安排了重启?
How can I detect if a reboot is scheduled in the future after calling 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
),但我需要这样做而不需要再次调用重启(因为我可能不想在此时实际触发重启)。
我尝试使用参数 SM_SHUTTINGDOWN
调用 GetSystemMetrics
,但即使我知道这是预定的 returns false
/0
。我假设这是实际发生的关机事件,未按计划发生。
无论如何,我是否可以检测到此调用正在进行中,或者更一般地说,是否安排了重新启动?
我正在使用 C#/DllImport
/interop 调用这些方法,因此我需要一个可从 C# 进程访问的 api。
This answer here 包含有关通过 Powershell 检查 Windows 注册表以查看是否计划重启的信息:
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
除此之外,ExitWindowsEx
超时时间长得离谱(最长为 315360000
- 10 年),然后检查 ERROR_SHUTDOWN_IS_SCHEDULED
- 如果命令成功,那么你'我会想要(当然)AbortSystemShutdown
...
您可以使用 Windows Event Log API 访问 "System" 频道上的日志条目。该日志记录了登录请求关闭系统和中止系统关闭的请求。
这段代码展示了如何使用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;
}
}
}
我目前正在使用 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
),但我需要这样做而不需要再次调用重启(因为我可能不想在此时实际触发重启)。
我尝试使用参数 SM_SHUTTINGDOWN
调用 GetSystemMetrics
,但即使我知道这是预定的 returns false
/0
。我假设这是实际发生的关机事件,未按计划发生。
无论如何,我是否可以检测到此调用正在进行中,或者更一般地说,是否安排了重新启动?
我正在使用 C#/DllImport
/interop 调用这些方法,因此我需要一个可从 C# 进程访问的 api。
This answer here 包含有关通过 Powershell 检查 Windows 注册表以查看是否计划重启的信息:
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
除此之外,ExitWindowsEx
超时时间长得离谱(最长为 315360000
- 10 年),然后检查 ERROR_SHUTDOWN_IS_SCHEDULED
- 如果命令成功,那么你'我会想要(当然)AbortSystemShutdown
...
您可以使用 Windows Event Log API 访问 "System" 频道上的日志条目。该日志记录了登录请求关闭系统和中止系统关闭的请求。
这段代码展示了如何使用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;
}
}
}