При каких условиях RmGetList возвращает 2 для выходного параметра lpdwRebootReasons?
Задний план
Я разрабатываю установщик Inno Setup для установки службы Cygwin, и меня озадачивает поведение, которое я наблюдаю в API-интерфейсах Windows Restart Manager.
В частности, когда служба запущена (запущена с помощью cygrunsrv
утилита), функция API RmGetList возвращает 2 (RmRebootReasonSessionMismatch
) для своего lpdwRebootReasons
выходной параметр. Этот выходной параметр является перечислением типа RM_REBOOT_REASON
и описание на MSDN для RmRebootReasonSessionMismatch
значение:
One or more processes are running in another Terminal Services session.
Файл журнала Inno Setup содержит следующие строки:
RestartManager found an application using one of our files: <executable name>
RestartManager found an application using one of our files: <service name>
Can use RestartManager to avoid reboot? No (2: Session Mismatch)
Затем Inno Setup продолжает попытки заменить используемые файлы, как если бы Restart Manager вообще не использовался.
Я озадачен этим выходным значением, потому что на двух разных машинах, которые я тестировал (Windows 10 1909 x64 и Windows Server 2012 R2), пользователи терминального сервера / удаленного рабочего стола не вошли в систему.
Если я остановлю службу и запустил другой исполняемый файл (в наборе файлов, которые должен заменить установщик), RmGetList вернет 0 (RmRebootReasonNone
) за lpdwRebootReasons
, а Inno Setup отображает обычный диалог для используемых файлов и позволяет пользователю выбрать автоматическое закрытие их.
Обозреватель процессов показывает оба процесса (cygrunsrv.exe
и процесс, который он запускает), работающий в сеансе 0 и в System
уровень целостности. Оба являются исполняемыми файлами консольной подсистемы.
Вопросы
При каких условиях RmGetList возвращает 2 (
RmRebootReasonSessionMismatch
) для своегоlpdwRebootReasons
выходной параметр? (Я пытаюсь понять, почему это происходит, когда служба работает.)Приводит ли это значение к сбою всего сеанса Restart Manager или может Restart Manager продолжить работу, даже если он считает, что приложения работают в одном или нескольких разных сеансах?
2 ответа
Подсказка от модуля RestartManager PowerShell
Модуль RestartManager PowerShell (особая благодарность Хиту Стюарту) предоставляет простой интерфейс PowerShell для Restart Manager. Мои команды следующие:
Set-Location <path to Cygwin root directory>
Start-RestartManagerSession
Get-ChildItem . -File -Include *.exe,*.dll -Recurse | RegisterRestartManagerResource
Get-RestartManagerProcess
Stop-RestartManagerProcess
Эти команды производят следующий вывод:
Id : <process ID>
StartTime : <process start time>
Description : <executable started by cygrunsrv>
ServiceName :
ApplicationType : Console
ApplicationStatus : Running
IsRestartable : False
RebootReason : SessionMismatch
Id : <cygrunsrv process id>
StartTime : <cygrunsrv process start time>
Description : <description of service>
ServiceName : <service name>
ApplicationType : Service
ApplicationStatus : Running
IsRestartable : True
RebootReason : SessionMismatch
По какой-то причине Restart Manager видит cygrunsrv.exe
служебный процесс как перезапускаемый, но исполняемый файл, который он порождает, как не перезапускаемый. (Мне все еще любопытно, почему это вообще происходит.)
Несовершенная попытка обходного пути
Основываясь на этом наблюдаемом поведении, я сначала попробовал следующее обходное решение:
В сценарии Inno Setup
[Setup]
раздел, установите следующее:CloseApplications=yes CloseApplicationsFilter=*.chm,*.pdf RestartApplications=yes
В
CloseApplicationsFilter
Директива указывает, какие файлы регистрируются в диспетчере перезапуска. Примечание я не указываю*.exe
или*.dll
Вот; Я хочу указать вручную только определенные.exe
файлы в[Code]
раздел.Вызов Inno Setup
RegisterExtraCloseApplicationsResource
функция один раз для каждого.exe
файл в настройке, который НЕ будет порожденcygrunsrv
и поместите их вRegisterExtraCloseApplicationsResources
процедура события. Пример:[Code] Procedure RegisterExtraCloseApplicationsResources(); Begin RegisterExtraCloseApplicationsResource(False, ExpandConstant('{app}\bin\cygrunsrv.exe')); End;
Важно не регистрировать исполняемые файлы, порожденные cygrunsrv.exe
или любой из файлов Cygwin DLL, потому что это предотвратит запуск диспетчера перезапуска в Inno Setup.
Это решение далеко не идеально, потому что исполняемые файлы обычно запускаются cygrunsrv
, если запущен отдельно, не обнаруживаются Restart Manager (например, sshd.exe
). Например, новые сеансы SSH создаются в исполняемых файлах, которые нельзя перезапустить с помощью Restart Manager.
Лучшее решение
Я решил, что лучшим решением является обнаружение любых запущенных исполняемых файлов из кода и подсказка пользователю, кроме функциональности Restart Manager (которая, проще говоря, не работает для служб Cygwin).
По вопросу 2 в документе RM_PROCESS_INFO
b
TRUE, если приложение может быть перезапущено диспетчером перезапуска; в противном случае - ЛОЖЬ. Этот член всегда имеет значение ИСТИНА, если процесс является службой. Этот член всегда имеет значение FALSE, если процесс является критическим системным процессом.
Это значение указывает, можно ли перезапустить приложение с помощью диспетчера перезапуска.
Для вопроса 1 обратите внимание, что службы работают в сеансе 0. Если процесс, занимающий ресурс (зарегистрированный в RmRegisterResources
) - услуга A, RmGetList
функция, которая также работает в сервисном процессе B, вернет lpdwRebootReasons = RmRebootReasonNone
, bRestartable = TRUE
.
Но если A не является службой, тогда A и B работают в разных сеансах, lpdwRebootReasons = RmRebootReasonSessionMismatch
а также bRestartable = FALSE
другие результаты:(B запустить с повышенными привилегиями)
- A & B - это консоль, находящаяся в одном сеансе :
lpdwRebootReasons = RmRebootReasonNone
,bRestartable = TRUE
,ApplicationType = RmConsole
. - A & B - это консоль и в другой сессии :
lpdwRebootReasons = RmRebootReasonSessionMismatch
,bRestartable = FALSE
,ApplicationType = RmConsole
. - A: сервис, B: консоль:
lpdwRebootReasons = RmRebootReasonNone
,bRestartable = TRUE
,ApplicationType = RmService
(B не запускается с повышенными привилегиями):
- A & B - это консоль и в другой сессии :
lpdwRebootReasons = RmRebootReasonCriticalProcess
,bRestartable = FALSE
,ApplicationType = RmCritical
. - A: сервис, B: консоль:
lpdwRebootReasons = RmRebootReasonPermissionDenied
,bRestartable = FALSE
,ApplicationType = RmCritical
Согласно документу bRestartable
зависит от ApplicationType
. И тогда мы видим, что еслиbRestartable = TRUE
, то lpdwRebootReasons = RmRebootReasonNone
. Но когдаbRestartable = FALSE
, Это зависит от других участников RM_PROCESS_INFO
.