Как поймать исключение и остановить службу Topshelf?

У меня есть служба windows topshelf, где я хочу выполнить некоторую проверку (т.е. если файл xml существует), и если проверка завершилась неудачно, мне нужно остановить службу Windows.

Поэтому я попытался выполнить проверку в методе Start(), а затем создать исключение:

public void Start()
{
    if (!File.Exists(_xmlFile) throw new FileNotFoundException();
    // Do some work here if xml file exists.
}

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

Есть ли способ не запускать службу, если выполняются определенные условия (т.е. файл не найден)?

Ответ 1

Я "заимствовал" образец кода для функциональной настройки topshelf, чтобы продемонстрировать точку:

HostFactory.Run(x =>                                 //1
    {
        x.Service<TownCrier>(s =>                        //2
        {
           s.ConstructUsing(name=> new TownCrier());     //3
           s.WhenStarted(tc => tc.Start());              //4
           s.WhenStopped(tc => tc.Stop());               //5
        });
        x.RunAsLocalSystem();                            //6

        x.SetDescription("Sample Topshelf Host");        //7
        x.SetDisplayName("Stuff");                       //8
        x.SetServiceName("stuff");                       //9
    });    

Вам нужно будет поместить вашу проверку файловой системы ДО вышеприведенного кода. Подумайте об этом секунду. Суть службы заключается в том, чтобы убедиться, что она БЕСПЛАТНО и БЕСПЛАТНО. В первую очередь вы пытаетесь подорвать базовый принцип использования сервисных приложений. Вместо того, чтобы пытаться остановить службу из-за отсутствующего файла, определите способ оповещения вашего персонала службы поддержки и НЕ выполняйте все, что зависит от этого отсутствующего файла.

Ответ 2

Вы можете использовать объект HostControl и изменить свой метод следующим образом:

public bool Start(HostControl hostControl)
{
    if (!File.Exists(_xmlFile) 
    {
        hostControl.Stop();
        return true;
    }

    // Do some work here if xml file exists.
    ...
}

И вам нужно передать HostControl в метод Start, например:

HostFactory.Run(conf =>
{
    conf.Service<YourService>(svcConf =>
    {
        svcConf.WhenStarted((service, hostControl) =>
        {
            return service.Start(hostControl);
        }
    }
}

Ответ 3

Каждый из методов WhenXxx может также принимать аргумент интерфейса HostControl, который может использоваться для запроса остановки службы, запроса дополнительного времени запуска/остановки и т.д.

В этом случае заменить подпись start() на bool start (HostControl hc). Сохраните ссылку на этот HostControl в службе следующим образом:

public bool Start(HostControl hc)
{
    hostControl = hc;
    Restart();
    return true;
}

Теперь, когда вы хотите остановить службу, используйте следующий вызов:

hostControl.Stop();

Ответ 4

Я просто столкнулся с этой проблемой, и все вышеперечисленные ответы, похоже, усложняют ситуацию. Все, что вам нужно сделать, это использовать перегрузку WhenStarted, которая принимает Func<T,HostControl,bool>, и возвращает false, если ваш внутренний загрузочный бутстрап не прошел. Я не думаю, что hostControl.Stop() нужно явно вызывать.

//Here is bit from inside the .Service<T>() call
s.WhenStarted((YourService svc, HostControl hc) => svc.Start());

//And the svc.Start method would look something like this:
class YourService
{
   public bool Start() {
     //return true if all is well
     //or false if you want service startup to be halted
   }
}

Ответ 5

Мне было любопытно об этом с точки зрения лучшей практики или рекомендации в документации Topshelf, но ничего не удалось найти. Однако я нашел два отдельных комментария от phatboyg...

Лучший комментарий... как остановить службу по исключению, через этот вопрос (я вырезал некоторые детали):

Если ваш метод "Пуск" службы вызывает исключение, служба не запустится.

Как только служба будет запущена, если будет отменено необработанное исключение, служба остановится и сообщит об этом в качестве сбоя диспетчеру управления сервисом.

Если вам необходимо программно остановить службу, используйте метод HostControl Stop.

Поэтому я считаю, что самый простой ответ - это исключение.

Вы делали это, и вы упоминаете, что "служба Windows остается в виде процесса за исключением". Это похоже на несвязанную ошибку где-то в вашем коде, или, возможно, у вас как-то было многопользовательских экземпляров? Я тестировал эти сценарии сегодня утром и не видел, чтобы мой сервис работал после того, как выбрал исключение в методе start.

Также, относящийся к проверке перед HostFactory.Run, упомянутый в принятом ответе, через https://groups.google.com/forum/embed/#!topic/topshelf-discuss/nX97k3yOhJU:

"Ваше приложение должно ничего не делать, кроме как настроить NLog/Log4Net перед вызовом метода HostFactory.Run().

Ответ 6

Когда вы поймаете исключение, вы можете использовать метод ServiceBase.Stop(), чтобы остановить службу самостоятельно.

try
{
    // Your Code
}
catch (Exception ex)
{
    // The code for stopping service
}

Кроме того, вы можете иметь несколько блоков catch в некоторых случаях:

try
{
    // Your Code
}
catch (IndexOutOfRengeException ex)
{
    // The code for stopping service
}
catch (FileNotFoundException exc)
{
    // The code for stopping service
}

Подробнее о ServiceBase.Stop()