Подтвердить что ты не робот

Каков надлежащий способ отказа службы Windows?

Я унаследовал службу Windows, написанную на С#. В редких случаях это терпит неудачу. Тем не менее, совсем не ясно, как неудачно пройти. Росс Беннетт излагает проблему элегантно на bytes.com. Для простоты я просто приведу его здесь.

Ахой, народ!

Я искал все это, но я просто не могу пошатнуть документацию из MSDN или из Google. Я просмотрел каждый .NET. статья о разработке служб Windows в MSDN, который я нашел.

Я разрабатываю службу Windows выражение. Эта служба читает данные конфигурации из системы реестра (HKLM), где он был депонирован другим "менеджером". нет проблемы там.

Служба использует рабочий поток для выполнения это работает. Нить создается в OnStart() и сигнализация/соединение/расположение в OnStop(). Опять же, никаких проблем.

Все работает красиво, когда:

  • Системный администратор настроил все правильно и
  • Доступ к внешним ресурсам сети возможен.

Но, конечно, мы, как разработчики, просто не может полагаться на:

  • Системный администратор настроил все правильно, или
  • доступны доступные ресурсы внешней сети.

Действительно, нам нужно, чтобы сервисное приложение имеет какой-то способ умирать самостоятельно. Если сеть ресурс опускается, нам нужно сервис для остановки. Но больше Нам нужен SCM, чтобы знать, что он остановился сам по себе. Потребности SCM знать, что служба "не удалось"... и не просто закрыт вниз кем-то.

Вызов "return" или вызов исключение в методе OnStart() даже не помогает службам в процессе запуска. SCM идет весело, и процесс держится работает в диспетчере задач - хотя это фактически не делает ничего, поскольку рабочий поток никогда не был создан и начал.

Использование экземпляра ServiceController тоже не делает этого. Это похоже на SCM как нормальное закрытие - не отказ службы. Поэтому ни одна из происходят действия по восстановлению или перезапуска. (Кроме того, есть документация MSDNful предупреждение об опасностях Поток потомков ServiceBase с использованием ServiceController, чтобы делать вещи происходят с самим собой.)

Я читал статьи, в которых люди возиться с вызовами PInvoking нативный код, чтобы установить Флаг статуса "Остановлен" в SCM. Но что не завершает процесс служба работает внутри.

Мне бы очень хотелось знать предполагаемый Способ:

  • Выключение службы из службы, где
  • SCM присваивается уведомление о том, что служба "остановлена", и
  • Процесс исчезает из диспетчера задач.

Решения с участием ServiceControllers не кажутся подходящими, если только потому что 2 не выполняется. (Что Рамочная документация противопоказания, которые кстати, неплохая масса.)

Буду признателен за любые рекомендации, указатели на документацию или даже хорошо обоснованная гипотеза.:-) Ой! А также Я с удовольствием развлекаю это Я пропустил этот момент.

Наиболее сердечно,

Росс Беннетт

4b9b3361

Ответ 1

Лучшей практикой в ​​собственном коде является вызов SetServiceStatus с ненулевым кодом выхода, чтобы указать 1) он остановился и 2) что-то пошло не так.

В управляемом коде вы можете добиться такого же эффекта, получив дескриптор SCM через свойство ServiceBase.ServiceHandle и P/Вызвать API Win32.

Я не понимаю, почему SCM будет рассматривать это иначе, чем установка свойства ServiceBase.ExitCode, отличного от нуля, а затем вызывающего ServiceBase.Stop. P/Invoke является немного более прямым, возможно, если служба находится в режиме паники.

Ответ 2

Я обнаружил, что Environment.Exit(1) отлично работает для меня. Обычно я помещаю его в метод, который ловит необработанные исключения и регистрирует проблему до того, как я ее остановлю. Он полностью разрушает сервис, но SCM также знает, что он выключен. Вы можете настроить SCM для автоматического возобновления службы, когда она уменьшится х раз. Я считаю, что это гораздо более полезно, чем писать собственный код перезапуска/завершения работы.

Ответ 3

Я не знаю, есть ли эквивалент (не-P/Invoke) для этого, но способ WinAPI, похоже, должен вызвать SetServiceStatus со значением SERVICE_STOPPED, а затем дождаться закрытия SCM вы вниз. Как положительный побочный эффект, он регистрирует отказ вашей службы в журнале событий.

Вот некоторые цитаты из соответствующей части документации:

Если служба вызывает SetServiceStatus с членом dwCurrentState, установленным на SERVICE_STOPPED, а член dwWin32ExitCode установлен на ненулевое значение, в журнал событий системы записывается следующая запись:

[...] <ServiceName> завершено со следующей ошибкой: <ExitCode> [...]

При вызове этой функции рекомендуется использовать следующие методы:

[...]

  • Если статус SERVICE_STOPPED, выполните всю необходимую очистку и вызовите SetServiceStatus только один раз. Эта функция вызывает вызов LRPC в SCM. Первый вызов функции в состоянии SERVICE_STOPPED закрывает дескриптор контекста RPC, и любые последующие вызовы могут привести к сбою процесса.
  • Не пытайтесь выполнить какую-либо дополнительную работу после вызова SetServiceStatus с SERVICE_STOPPED, так как процесс обслуживания может быть завершен в любое время.

PS: На мой взгляд, если сетевые ресурсы недоступны, служба не должна останавливаться, а продолжать работать, ожидая, когда ресурсы станут доступными. Временные отключения сети могут произойти, и они не должны требовать ручного вмешательства от системного администратора после восстановления сети.

Ответ 4

Вы можете получить правильный ExitCode, как описано здесь. Поэтому диспетчер служб Windows даст правильный текст ошибки.

My OnStart в ServiceBase выглядит следующим образом:

protected override void OnStart(string[] args)
{
    try
    {
        DoStart();
    }
    catch (Exception exp)
    {
        Win32Exception w32ex = exp as Win32Exception;
        if (w32ex == null)
        {
            w32ex = exp.InnerException as Win32Exception;
        }
        if (w32ex != null)
        {
            ExitCode = w32ex.ErrorCode;
        }
        Stop();
    }
}

Ответ 5

После некоторого тестирования я нашел следующие работы в случаях, когда вы вызываете Stop, могут вызывать другие проблемы:

        ExitCode = 1;
        Environment.Exit(1);

Просто вызывается Environment.Exit не делает SCM выполнять обработку ошибок, но сначала устанавливает ExitCode ServiceBase.