При каких условиях RmGetList возвращает 2 для выходного параметра lpdwRebootReasons? - PullRequest
3 голосов
/ 24 января 2020

Фон

Я разрабатываю установщик Inno Setup для установки службы Cygwin , и меня озадачивает поведение, которое я наблюдаю в Windows Диспетчер перезапуска API.

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

Process Explorer показывает оба процесса (cygrunsrv.exe и процесс, который он запускает), выполняющиеся в сеансе 0 и на уровне целостности System. Оба являются исполняемыми файлами консольной подсистемы.

Вопросы

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

  2. Приводит ли это значение к сбою всего сеанса Restart Manager, или может продолжить работу Restart Manager, даже если он считает, приложения работают в одном или нескольких разных сеансах?

Ответы [ 2 ]

3 голосов
/ 27 января 2020

Для вопроса 2 в документе RM_PROCESS_INFO

bRestartable

TRUE , если приложение может быть перезапущено Перезапустить менеджер; в противном случае FALSE . Этот член всегда ИСТИНА , если процесс является службой. Этот элемент всегда 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.

1 голос
/ 27 января 2020

Подсказка из модуля PowerShell RestartManager

Модуль PowerShell RestartManager (особая благодарность Хит Стюарт ) обеспечивает простой интерфейс PowerShell для менеджера перезапуска. Мои команды следующие:

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

По какой-то причине Менеджер перезапуска видит процесс службы cygrunsrv.exe как перезапускаемый, но исполняемый он появляется как не перезапускаемый. (Мне все еще интересно, почему это происходит в первую очередь.)

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

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

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

    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 или любой из файлов DLL Cygwin, так как это не позволит перезапустить Диспетчер перезапуска в Inno Setup. .

Это решение далеко от совершенства, поскольку исполняемые файлы, обычно запускаемые с cygrunsrv, если запускаются отдельно, не обнаруживаются Restart Manager (например, sshd.exe). Например, новые сеансы S SH создаются в исполняемых файлах, которые не могут быть перезапущены с помощью диспетчера перезапуска.

Лучшее решение

Я решил, что лучшим решением будет обнаружение любых запущенных исполняемых файлов из кода и запросить пользователя, кроме функций Restart Manager (которые, проще говоря, не работают для сервисов Cygwin).

...