Как служба Windows может программно перезапустить себя?

135

Мне нужно написать надежный код в .NET, чтобы служба Windows (сервер 2003) перезапустить себя. Каков наилучший способ так это? Есть ли .NET API для этого?

Рон Харлев
источник
7
«Надежный» - это слово ласки ( stackoverflow.fogbugz.com/default.asp?W13086 ) - какие конкретные черты вам требуются?
Эйдан Райан

Ответы:

187

Настройте сервис на перезапуск после сбоя (дважды щелкните сервис на панели управления и посмотрите на эти вкладки - я забыл его название). Затем в любое время, когда вы захотите перезапустить сервис, просто позвоните Environment.Exit(1)(или любой ненулевой возврат), и ОС перезапустит его для вас.

TheSoftwareJedi
источник
7
Fyi расположение находится на панели служб, щелкните правой кнопкой мыши по соответствующей службе и выберите свойства, затем выберите вкладку восстановления.
Джеймс Майкл Хэйр
20
Я вижу, как это достигает желаемого поведения, но код возврата 1 предназначен для сообщения системе об ошибке. Разве это не плохая практика, если на самом деле не было ошибок и вы просто хотели перезапустить службу?
mgttlinger
4
Это может быть сделано программно установщиком службы в событии после установки, не нужно нажимать вокруг ... Что, если ваша служба находится на удаленном сервере, и вам нужно устанавливать ее несколько раз в день, например, во время тестирования?
Дин Куга
5
@mgttlinger: я думаю, что да, это плохая практика, когда ваш сервис здоров, поэтому его не нужно будет перезапускать. Но некоторые сервисные архитектуры находятся за пределами нашей досягаемости, и если их нужно перезапустить, это признак того, что это не очень хорошо, поэтому нет проблем закрыть и освободить некоторые минимальные ресурсы (если это возможно) и «порезать ноги», так как оставляя служба работает неправильно, может быть хуже (бесполезно).
Лучано
5
@JohnLeidegren, если вы хотите правильно завершить работу, вы можете установить Service.ExitCode = 1и затем выполнить, Service.Stop()а также включить флажок «Включить действия для остановок с ошибками» на экране настроек восстановления службы. Делая это таким образом, вы сможете правильно завершить работу и запустить перезагрузку.
Крис Райс
22
Dim proc As New Process()
Dim psi As New ProcessStartInfo()

psi.CreateNoWindow = True
psi.FileName = "cmd.exe"
psi.Arguments = "/C net stop YOURSERVICENAMEHERE && net start YOURSERVICENAMEHERE"
psi.LoadUserProfile = False
psi.UseShellExecute = False
psi.WindowStyle = ProcessWindowStyle.Hidden
proc.StartInfo = psi
proc.Start()
Халид Рахаман
источник
3
Не забудьте добавить "" вокруг имени службы, если оно содержит пробелы.
АРС
4
Это только для служб, работающих под привилегированной учетной записью, что в любом случае является плохой идеей.
Дмитрий Гусаров
17

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

Грег Хьюгилл
источник
Несмотря на то, что я столкнулся с той же проблемой, что и +1, при просмотре sc.exe выясняется, что он использует SERVICE_QUERY_CONFIG в качестве dwDesiredAccess при вызове OpenSCManager (), а затем вызывает OpenService () с SERVICE_START | SERVICE_STOP, чтобы открыть определенный сервис, и он работает нормально (нет проблем с доступом). Не уверен, как это можно сделать в .NET.
n0p
Большинство служб Windows работают как система, поэтому это не должно быть проблемой.
Переключатель
@Switch: существует огромный круг сервисов, работающих под NETWORK SERVICE, который по умолчанию даже не имеет прав на открытие собственного исполняемого файла.
AgentFire
@AgentFire, правильно, но по умолчанию используется Local System, а не NETWORK SERVICE. Локальная система по своей природе имеет наивысший уровень привилегий для операционной системы - превышающий те, которые назначены членам локальной группы администраторов.
Переключить
@ Переключение, но использование самых доступных привилегий нарушает принцип PoL .
AgentFire
12

Вы можете создать процесс, который является командной строкой DOS, который перезапускает себя:

 Process process = new Process();
 process.StartInfo.FileName = "cmd";
 process.StartInfo.Arguments = "/c net stop \"servicename\" & net start \"servicename\"";
 process.Start();
VladL
источник
Это, без каких-либо других действий, унаследует контекст безопасности вызывающего потока ... и до тех пор, пока у этого контекста достаточно мощности для перезапуска, у вас все хорошо.
Глина
12
const string strCmdText = "/C net stop \"SERVICENAME\"&net start \"SERVICENAME\"";
Process.Start("CMD.exe", strCmdText);

где SERVICENAME- название вашего сервиса (двойные кавычки, включенные для учета пробелов в имени сервиса, в противном случае могут быть опущены).

Чисто, не требуется автоматическая перезагрузка конфигурации.

Filip
источник
9
узнал что-то новое о & vs &&: [command1] & [command2] всегда будет выполнять обе команды последовательно, в то время как [command1] && [command2] запускает command2 только в том случае, если command1 выполняется успешно ( autoitscript.com/forum/topic/… )
Джеффри Рыцарь
5

Это будет зависеть от того, почему вы хотите перезапустить себя.

Если вы просто ищете способ периодически очищать службу, то у вас может быть запущен таймер, который периодически вызывает процедуру очистки.

Если вы ищете способ перезапуска при сбое - сервисный хост сам может предоставить эту возможность при настройке.

Так зачем вам перезагружать сервер? Чего ты пытаешься достичь?

Броуди
источник
1
Это не сработает, если вас интересует куча больших объектов и фрагментация памяти. Предположительно .NET 4 / 4.5 и 64-битные процессы очень помогли в этом.
Дэвид Касса
3

Я не думаю, что вы можете сделать это в автономной службе (когда вы вызываете Restart, она останавливает службу, что прерывает команду Restart и больше никогда не запускается). Если вы можете добавить второй .exe-файл (консольное приложение, использующее класс ServiceManager), то вы можете запустить автономный .exe-файл, запустить его, а затем выйти.

Подумав еще раз, вы, вероятно, могли бы заставить службу зарегистрировать запланированное задание (например, с помощью команды 'at' из командной строки), чтобы запустить службу и затем остановить ее самостоятельно; это, вероятно, сработает.

технофилов
источник
3

Проблема с выделением командного файла или EXE заключается в том, что служба может иметь или не иметь разрешения, необходимые для запуска внешнего приложения.

Самый чистый способ сделать это - использовать метод OnStop (), который является точкой входа для Service Control Manager. Тогда весь ваш код очистки будет запущен, и у вас не будет зависающих сокетов или других процессов, если ваш код остановки выполняет свою работу.

Для этого перед завершением вам нужно установить флаг, который сообщает методу OnStop для выхода с кодом ошибки; тогда SCM знает, что служба должна быть перезапущена. Без этого флага вы не сможете вручную остановить службу из SCM. Это также предполагает, что вы настроили службу для перезапуска в случае ошибки.

Вот мой код остановки:

...

bool ABORT;

protected override void OnStop()
{
    Logger.log("Stopping service");
    WorkThreadRun = false;
    WorkThread.Join();
    Logger.stop();
    // if there was a problem, set an exit error code
    // so the service manager will restart this
    if(ABORT)Environment.Exit(1);
}

Если служба сталкивается с проблемой и требует перезапуска, я запускаю поток, который останавливает службу от SCM. Это позволяет сервису убирать за собой:

...

if(NeedToRestart)
{
    ABORT = true;
    new Thread(RestartThread).Start();
}

void RestartThread()
{
    ServiceController sc = new ServiceController(ServiceName);
    try
    {
        sc.Stop();
    }
    catch (Exception) { }
}
user3235770
источник
2

Я бы использовал планировщик Windows, чтобы запланировать перезапуск вашего сервиса. Проблема в том, что вы не можете перезапустить себя, но вы можете остановить себя. (Вы, по сути, отпилили ветку, на которой сидите ... если вы поймете мою аналогию) Вам нужен отдельный процесс, чтобы сделать это для вас. Планировщик Windows является подходящим. Запланируйте одноразовое задание для немедленного запуска службы (даже из самой службы).

В противном случае вам придется создать «пастырский» процесс, который сделает это за вас.

dviljoen
источник
2

Первый ответ на этот вопрос - самое простое решение: «Environment.Exit (1)». Я использую его в Windows Server 2008 R2, и он отлично работает. Служба останавливается, O / S ждет 1 минуту, затем перезапускает.

tangentMan
источник
Как долго он ждет, зависит от того, как долго вы настроили его ждать. По умолчанию это 1 минута, но если кто-то не хочет, ваш ответ произведет неправильное впечатление.
Espen
1

Я не думаю, что это возможно. Когда служба «остановлена», она полностью выгружается.

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

ТЕД
источник
0

Лучшим подходом может быть использование службы NT в качестве оболочки для вашего приложения. Когда служба NT запущена, ваше приложение может запускаться в режиме ожидания, ожидая запуска команды (или быть настроено на автоматический запуск).

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

plamb
источник
0

Просто мимоходом и подумал, что я хотел бы добавить дополнительную информацию ...

Вы также можете выдать исключение, это автоматически закроет службу Windows, и параметры автоматического перезапуска просто включаются. Единственная проблема с этим заключается в том, что если у вас есть среда разработки на вашем компьютере, то JIT пытается включить, и вы получите подсказку отладки Y / N. скажи нет, и тогда он закроется, а затем перезапустится правильно. (на ПК без JIT все просто работает). причина, по которой я троллю, заключается в том, что этот JIT является новым для Win 7 (раньше он работал нормально с XP и т. д.), и я пытаюсь найти способ отключить JIT .... я могу попробовать упомянутый здесь метод Environment.Exit и посмотреть, как это тоже работает.

Кристиан: Бристоль, Великобритания

Kristian
источник
3
Все эти решения исключений, exit (1) позволяют избежать надлежащей очистки приложения.
Недобросовестный
0

Создайте файл restart.bat следующим образом

@echo on
set once="C:\Program Files\MyService\once.bat"
set taskname=Restart_MyService
set service=MyService
echo rem %time% >%once%
echo net stop %service% >>%once%
echo net start %service% >>%once%
echo del %once% >>%once%

schtasks /create /ru "System" /tn %taskname% /tr '%once%' /sc onstart /F /V1 /Z
schtasks /run /tn %taskname%

Затем удалите задачу% taskname% при запуске% service%

Эрик Мартино
источник
0

Создайте отдельный домен приложения для размещения кода приложения. Когда требуется перезагрузка, мы можем выгрузить и перезагрузить домен приложения вместо процесса (служба Windows). Вот как работает пул приложений IIS: они не запускают приложение asp.net напрямую, они используют отдельный appdmain.

CreativeManix
источник
-2

Самый простой способ - создать командный файл с:

чистый стоп чистый старт

и добавьте файл в планировщик с желаемым интервалом времени


источник
Это не работает, потому что пакет останавливается между обеими командами, потому что это дочерний процесс самой службы.
Orabîg
-3
private static void  RestartService(string serviceName)
    {
        using (var controller = new ServiceController(serviceName))
        {
            controller.Stop();
            int counter = 0;
            while (controller.Status != ServiceControllerStatus.Stopped)
            {
                Thread.Sleep(100);
                controller.Refresh();
                counter++;
                if (counter > 1000)
                {
                    throw new System.TimeoutException(string.Format("Could not stop service: {0}", Constants.Series6Service.WindowsServiceName));
                }
            }

            controller.Start();
        }
    }
Ли Смит
источник