I'm currently triggering a reboot using the windows api call InitiateSystemShutdownW
. I'm passing this a dwTimeout
value (10 minutes). I want to detect if this reboot (or any other reboot) has been scheduled:
[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);
I know that if I call it again I will receive an error message that tells me it's already scheduled (via GetLastWin32Error
), but I need to do this without calling reboot again (because I may not want to actually trigger a reboot at this point).
I've tried calling GetSystemMetrics
with the parameter SM_SHUTTINGDOWN
but even though I know this is scheduled this returns false
/0
. I'm presuming this is an actual shutdown happening not scheduled to happen.
Is there anyway I can detect that this call is in progress or more generally whether a reboot is scheduled or not?
I'm calling these methods using C#/DllImport
/interop so I need an api that is accessible from a c# process.
You can use the Windows Event Log API to access log entries on the "System" channel. That log records requests by a login to shutdown the system, and to abort a system shutdown.
This code shows how to use P/Invoke to get the number of system shutdown and shutdown abort requests within a specified timeframe before the query (in milliseconds). So if you have 2 scheduled shutdown requests in the last hour, and 1 abort request, then you have a pending shutdown in progress.
If you really want all of the details about a logged shutdown request, then you can use the EvtRender function to pull out the data in XML, and parse it. This code doesn't do that.
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;
}
}
}