В настоящее время я запускаю перезагрузку, используя вызов 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), но мне нужно сделать это без повторного вызова перезагрузки (потому что я могу не захотеть на самом деле запускать перезагрузку в этот момент) .
Я пытался вызвать GetSystemMetrics с параметром SM_SHUTTINGDOWN, но хотя я знаю, что это запланировано, возвращается false/0. Я предполагаю, что это фактическое отключение, которое не запланировано.
Могу ли я в любом случае определить, что этот вызов выполняется, или, в более общем плане, запланирована ли перезагрузка или нет?
Я вызываю эти методы с помощью C#/DllImport/interop, поэтому мне нужен API, доступный из процесса C#.





Этот ответ здесь содержит информацию о проверке реестра Windows через Powershell, чтобы узнать, запланирована ли перезагрузка:
Test-Path 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending'
Кроме того, ExitWindowsEx со смехотворно долгим тайм-аутом (максимум 315360000 - 10 лет), затем проверка на ERROR_SHUTDOWN_IS_SCHEDULED - если команда выполнена успешно, то вам захочется (конечно) AbortSystemShutdown...
В этом ответе говорится, что это не вернет true, если перезагрузка запланирована с помощью shutdown.exe, она вернет true, только если Windows скажет, что ваш компьютер необходимо перезагрузить. shutdown.exe — это просто оболочка для InitiateSystemShutdownW. Я проверяю, но я думаю, что это неправильно. Я также указал в вопросе, что не хочу запускать перезагрузку (долго или нет)
Приносим свои извинения и сожаления по поводу отрицательных голосов: \ Но один из ваших вопросов гласит: «Могу ли я каким-либо образом определить, что этот вызов выполняется или, в более общем случае, запланирована ли перезагрузка или нет?»
Но это не обнаруживает «общие» запланированные перезагрузки, хотя делает это, иначе это сработало бы для меня, общего типа, неспецифического. Он обнаруживает этот конкретный сценарий. Это не то, что означает общее. Если вы думаете, что это дублировать, то вы также не должны добавлять ответ
У метода «смехотворно долгого тайм-аута» есть недостаток, заключающийся в том, что он загрязняет журнал событий.
Вы можете использовать API журнала событий Windows для доступа к записям журнала на канале «Система». В этом журнале записываются запросы входа в систему на выключение системы и прерывание выключения системы.
В этом коде показано, как использовать 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;
}
}
}
Кажется, моя рабочая нагрузка была «перераспределена», поэтому мне придется проверить это в другой раз. Хотя, похоже, разумное решение, спасибо.
Теоретически тайм-аут выключения может составлять до 10 лет — максимальный тайм-аут составляет 3650 дней минус одна секунда — и поэтому этот метод может не найти сообщение о завершении работы в журнале событий, потому что он не оглядывается назад достаточно далеко (как представлено). он оглядывается только на 1 час назад), или даже событие выключения было так давно, что было удалено из журнала. (На практике эта проблема маловероятна, потому что вероятность отключения на более короткие периоды времени, например, на 1 час или 5 минут, гораздо выше.)
Почему бы вам вместо этого не использовать класс Eventlog()?
Внутри winlogon.exe есть переменные, записывающие ожидаемое время выключения, сообщение, флаги и т. д., но нет общедоступного API для извлечения этой информации из него (кроме использования журнала событий). В Windows действительно должно быть что-то вроде API GetPendingShutdownDetails. Я создал задачу центра обратной связи, попросив Microsoft добавить ее — aka.ms/AAcdp86