Скажем, если я открою Блокнот, наберу что-то в нем и не сохраню его, затем вызову следующий API из того же сеанса пользователя:
ExitWindowsEx(EWX_LOGOFF, SHTDN_REASON_MAJOR_OTHER | SHTDN_REASON_MINOR_OTHER | SHTDN_REASON_FLAG_PLANNED);
Этот пользовательский сеанс войдет в «состояние выключения», где ОС покажет окно наложения, отображающее сообщение о том, что Блокнот не позволяет системе выйти из системы. Это наложение не исчезнет, пока пользователь не нажмет кнопки «Отмена» или «Принудительный выход».
Итак, вопрос из двух частей:
Есть ли способ узнать, какие процессы заблокировали процесс выхода из системы / завершения работы?
Есть ли какой-нибудь способ отменить это «состояние выключения» пользовательского сеанса программно?
PS. Это состояние может быть обнаружено с помощью вызова GetSystemMetrics (SM_SHUTTINGDOWN);
РЕДАКТИРОВАТЬ: Вопреки ответу ниже, я я не пытаясь остановить отключение системы, а также то, что какой-либо процесс пользовательского режима «завис».
EDIT2: Вот скриншот наложения, которое я пытаюсь отменить / закрыть:
Вопрос 2: «Можно ли программно отменить это состояние выключения?»
Суть в том, что ответ не совсем. И при этом вы не должны по-настоящему хотеть прекращать отключение программным путем, ЕСЛИ НЕ БУДЕТ: завершение работы приведет к серьезной потере данных или существенно повлияет на работу пользователя при последующем запуске системы. Но упомяну только один пример: представьте, что компьютер перегревается — программная остановка может привести к перегреву системы (и очень сердитому пользователю).
Выключение системы также не единственное, что вам нужно контролировать. Также есть события гибернации и приостановки (посмотрите сообщение WM_POWERBROADCAST).
Тем не менее, Windows предоставляет множество механизмов для обнаружения выключения системы. Например:
Если в вашем приложении есть насос сообщений, вы можете выбрать ЛОЖЬ, когда Windows опрашивает запущенные приложения для голосования. WM_QUERYENDSESSION , однако Windows начиная с Vista по-прежнему будет принудительно завершать работу после истечения времени ожидания. Начиная с Vista вы можете (и должны) ShutdownBlockReasonCreate после возврата false в WM_QUERYENDSESSION.
Если ваше приложение работает как сервис, вы можете использовать RegisterServiceCtrHandlerEx а потом SetServiceStatus чтобы получить отсрочку продления на 3 минуты, установив SERVICE_ACCEPT_PRESHUTDOWN, которая получит вам уведомление SERVICE_CONTROL_PRESHUTDOWN. Естественно, вы не получите уведомление о выходе из системы, потому что служба не зависит от выхода из системы. До Vista вы можете зарегистрироваться для уведомления SERVICE_CONTROL_SHUTDOWN.
Консольные приложения (и приложения с графическим интерфейсом, но это не имеет смысла) могут использовать SetConsoleCtrlHandler получать уведомления о CTRL_LOGOFF и CTRL_SHUTDOWN_EVENT.
На гораздо более низком уровне можно попробовать подключить функции API, такие как NTShutdown или даже NtSetSystemPowerState, который, по-видимому, «последнее, что вызывается во время ЛЮБОГО типа перезагрузки». Но я бы настоятельно рекомендовал не пытаться это сделать.
Тем не менее, есть способы действительно настаивать на том, что система не должна быть закрыта.
Учтите следующее:
1.) Попробуйте зарегистрировать приложение, чтобы быть первым в очереди на получение уведомления о завершении работы. Что-то вроде:
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686227(v=vs.85).aspx
if(!SetProcessShutdownParameters(0x4ff, 0)) // greedy highest documented System reserved FirstShutdown
{
// Fallback
if(!SetProcessShutdownParameters(0x3ff, 0)) // highest notification range for applications
{
// shouldn't happen
}
}
2.) Вернуть FALSE на WM_QUERYENDSESSION
Начиная с Vista, вызывайте ShutdownBlockReasonCreate () после возврата false на WM_QUERYENDSESSION.
3.) Скажите Windows, что вам нужна система, чтобы она работала и была доступна. Посмотри на
http://msdn.microsoft.com/en-us/library/windows/desktop/aa373208(v=vs.85).aspx
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED);
4.) Очистите, вызовите ShutdownBlockReasonDestroy () в Vista и далее, и ТОГДА полностью выключите систему.
Вы также можете попробовать недокументированную функцию (по крайней мере, она больше не в MSDN). CancelShutdown в «user32.dll», который в какой-то момент (до сих пор может) функционировать так же, как вызов shutdown.exe с флагом abort.
Ваш пробег может отличаться.
После вашей правки, которая проясняет ваш вопрос:
Если вы отслеживаете WM_QUERYENDSESSION и отвечаете на него ЛОЖЬ, вы можете опросить ваш все еще работающий процесс в течение заранее определенного периода времени, а затем выполнить вызов ExitWindowsEx с флагом EWX_FORCEIFHUNG, например на WM_ENDSESSION. Или вы могли бы действительно назвать это упреждающим при получении WM_QUERYENDSESSION — проблема в том, что, если принудительное завершение работы приводит к серьезной потере данных? Вы не делаете пользователю системы ничего хорошего в этот момент.
Обновите после ваших комментариев:
Как насчет этого:
Чтобы узнать блокирующее приложение:
Вы также можете попробовать использовать вышеописанную технику дескриптора окна, чтобы выяснить, можете ли вы получить дескриптор наложения «зависшее окно». Наличие этого может дать вам возможность сделать ключ отправки для отмены состояния. Держу пари, что вы не сможете получить к нему доступ, но я не пробовал 🙂
Опять ваш пробег может отличаться 🙂