При каких условиях 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уровень целостности. Оба являются исполняемыми файлами консольной подсистемы.

Вопросы

  1. При каких условиях RmGetList возвращает 2 (RmRebootReasonSessionMismatch) для своего lpdwRebootReasonsвыходной параметр? (Я пытаюсь понять, почему это происходит, когда служба работает.)

  2. Приводит ли это значение к сбою всего сеанса 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служебный процесс как перезапускаемый, но исполняемый файл, который он порождает, как не перезапускаемый. (Мне все еще любопытно, почему это вообще происходит.)

Несовершенная попытка обходного пути

Основываясь на этом наблюдаемом поведении, я сначала попробовал следующее обходное решение:

  1. В сценарии Inno Setup [Setup] раздел, установите следующее:

    CloseApplications=yes
    CloseApplicationsFilter=*.chm,*.pdf
    RestartApplications=yes
    

    В CloseApplicationsFilter Директива указывает, какие файлы регистрируются в диспетчере перезапуска. Примечание я не указываю*.exe или *.dllВот; Я хочу указать вручную только определенные.exe файлы в [Code] раздел.

  2. Вызов 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.

Другие вопросы по тегам