How to catch exception and stop Topshelf service?
Asked Answered
H

6

20

I have a topshelf windows service where I want to do some checking (i.e. if an xml file exists) and if the check fails I need the windows service to stop.

So I tried doing the check in the Start() method and then raise an exception:

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

However, the windows service stays around as a process after the exception which I then have to kill manually in the task manager.

Is there a way to not run the service if certain conditions (i.e. file not found) hold?

Hunley answered 21/5, 2014 at 19:2 Comment(7)
Why are you throwing an exception with nothing to catch it?Guttering
Also you don't need to throw FileNotFoundException clr will do it for you.Ticklish
I am trying to get the windows service to stop running on exception and was hoping that topshelf would stop it.Hunley
how are you starting the service? I found a topshelf service sample from an old project and it uses a HostFactory to start the serviceEdgar
@JD The point being made is that you gain nothing by explicitly throwing an exception. if the file is not there the CLR will throw the FileNotFound exception for you.Edgar
@Edgar : I am using HostFactory.Run()Hunley
Change to "if (!File.Exists(_xmlFile) return false;" and service will not start if the file does not exist.Profusion
E
3

I've "borrowed" the sample code for the functional setup of topshelf to demonstrate a point:

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
    });    

You're going to have to place your file system check BEFORE the above code runs. Let's think about this a second. The point of having a service is to make sure it RUNS and KEEPS RUNNING. You're attempting to subvert a basic principle of having service applications in the first place. Instead of trying to stop the service because of the missing file, figure out some way to alert your support staff and NOT do whatever depends on that missing file.

Eschew answered 21/5, 2014 at 19:24 Comment(6)
Thank you for the code. I tried that earlier but could not stop the service. I am currently logging so support staff have a way of know that it failed. I just did not want them to go and kill the service first.Hunley
@codenoire is right. Topshelf does not expose a way to call shutdown during the Start transition, and doing so could jam up your service manager. You'll need to check for the XML file and just return before even calling the HostFactory.Run to get the behaviour you're looking for.Uneducated
I'd disagree with this. You might have some preconditions to check that, if not fulfilled, running the service is pointless.Prithee
I was kind of curious what the author of Topshelf thinks about this scenario. I haven't found anything to answer the question, but did see this comment here: "Your application should do nothing more than configure NLog/Log4Net before calling the HostFactory.Run() method."Eellike
Topshelf DOES provide a way of shutting down in this way. See @julio.g's answer belowLamppost
Classic Stack Overflow stuff. "I want my service to stop itself" "You shouldn't want to do this you're subverting a basic principle". I'm the programmer for goodness sake let me choose whether to stop it or not. Thanks julio.g for some sanity below.Metchnikoff
P
35

You could use the HostControl object and modify your method like this:

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

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

And you will need to pass the HostControl in to the Start method like this:

HostFactory.Run(conf =>
{
    conf.Service<YourService>(svcConf =>
    {
        svcConf.WhenStarted((service, hostControl) =>
        {
            return service.Start(hostControl);
        }
    }
}
Prithee answered 30/1, 2015 at 15:49 Comment(4)
I'd suggest returning false in the failure case - this appears to make TopShelf report to the Service Control Manager that the service failed to start.Unearthly
There is no need for calling "hostControl.Stop()". Just return false inside the Start method will suffice.Profusion
This was helpful. The startup process for my application is longer than what I would consider acceptable waiting for the service itself to start. For that I return true from the Start method but hold onto the hostcontrol and call hostcontrol.Stop if my application is unable to complete the startup process.Uninstructed
This worked perfectly. It's poorly documented in the TopShelf docs though.Entozoic
S
8

Each of the WhenXxx methods can also take an argument of the HostControl interface, which can be used to request the service be stopped, request additional start/stop time, etc.

In such case, change signature of start() to be bool start(HostControl hc). Retain reference to this HostControl in the service as follow:

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

Now when you want to stop the service use following call:

hostControl.Stop();
Sadowski answered 13/4, 2015 at 15:44 Comment(0)
E
5

I was curious about this from the point of view of a best practice or recommendation in Topshelf's documentation, but couldn't find anything. I did, however, find two separate comments from phatboyg...

Best comment... how to stop service on exception, via this issue (I cut out some of the detail):

If your service's Start method throws an exception, the service will fail to start.

Once the service is running, if an unhandled exception is thrown, the service will stop, and report it as a crash to the service control manager.

If you need to Stop your service programatically, use the HostControl method Stop.

So I think the easiest answer is to throw an exception.

You were doing that, and you mention "the windows service stays around as a process after the exception". That seems like an unrelated bug somewhere in your code, or perhaps you somehow had multple instances running? I've been testing these scenarios this morning and have not seen my service running after throwing an Exception in the start method.

Also, relevant to checking before HostFactory.Run, mentioned in the accepted answer, via https://groups.google.com/forum/embed/#!topic/topshelf-discuss/nX97k3yOhJU:

"Your application should do nothing more than configure NLog/Log4Net before calling the HostFactory.Run() method."

Eellike answered 16/10, 2015 at 13:49 Comment(1)
I have tried this... throwing exception in the start method with stop the service... but it does not stop the service during the execution. I had to use hostControl.Stop() to stop the service, as explained by @julioStud
W
4

I just ran into this issue and all the above answers seem to be over complicating things. All you need to do is use the WhenStarted overload that accepts a Func<T,HostControl,bool> and return false if your internal service bootstrap failed. I don't think hostControl.Stop() needs to be called explicitly.

//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
   }
}
Walters answered 18/1, 2016 at 9:45 Comment(1)
I'd like to note that Topshelf ignores false value returned from Start method. Service is shown as started in the Services mmc. However exception thrown from Start stops serviceTeach
E
3

I've "borrowed" the sample code for the functional setup of topshelf to demonstrate a point:

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
    });    

You're going to have to place your file system check BEFORE the above code runs. Let's think about this a second. The point of having a service is to make sure it RUNS and KEEPS RUNNING. You're attempting to subvert a basic principle of having service applications in the first place. Instead of trying to stop the service because of the missing file, figure out some way to alert your support staff and NOT do whatever depends on that missing file.

Eschew answered 21/5, 2014 at 19:24 Comment(6)
Thank you for the code. I tried that earlier but could not stop the service. I am currently logging so support staff have a way of know that it failed. I just did not want them to go and kill the service first.Hunley
@codenoire is right. Topshelf does not expose a way to call shutdown during the Start transition, and doing so could jam up your service manager. You'll need to check for the XML file and just return before even calling the HostFactory.Run to get the behaviour you're looking for.Uneducated
I'd disagree with this. You might have some preconditions to check that, if not fulfilled, running the service is pointless.Prithee
I was kind of curious what the author of Topshelf thinks about this scenario. I haven't found anything to answer the question, but did see this comment here: "Your application should do nothing more than configure NLog/Log4Net before calling the HostFactory.Run() method."Eellike
Topshelf DOES provide a way of shutting down in this way. See @julio.g's answer belowLamppost
Classic Stack Overflow stuff. "I want my service to stop itself" "You shouldn't want to do this you're subverting a basic principle". I'm the programmer for goodness sake let me choose whether to stop it or not. Thanks julio.g for some sanity below.Metchnikoff
Y
-4

When you catch the exception you can use ServiceBase.Stop() Method to stop the service by itself.

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

Also you can have multi catch blocks in some cases:

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

Read more about ServiceBase.Stop()

Ymir answered 21/5, 2014 at 19:8 Comment(6)
Catch Exception then FileNotFoundException?Ticklish
@Edgar Why are you saying that? There is no reason to not working in windows services.Ymir
a windows service is not going to display a messageboxEdgar
Yes you are right. But the way I say to catch the exception is OK, isn't it? @EdgarYmir
The problem is not to catch exceptions, but to stop the service.Ecbolic
This is a TopShelf Service. There is no ServiceBase object that's easily accessible. He should be calling HostControl.Stop() instead.Interfuse

© 2022 - 2024 — McMap. All rights reserved.