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?

demongolem
  • 9,474
  • 36
  • 90
  • 105
JD.
  • 15,171
  • 21
  • 86
  • 159

6 Answers6

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);
        }
    }
}
Tom Ferguson
  • 907
  • 1
  • 10
  • 26
julio.g
  • 3,268
  • 5
  • 28
  • 25
  • 3
    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. – Chris Brook Jan 31 '17 at 18:41
  • 1
    There is no need for calling "hostControl.Stop()". Just return false inside the Start method will suffice. – user797717 Apr 02 '17 at 08:21
  • 1
    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. – SixOThree Apr 05 '17 at 20:22
  • This worked perfectly. It's poorly documented in the TopShelf docs though. – Fandango68 Mar 09 '21 at 01:16
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();
Tshilidzi Mudau
  • 7,373
  • 6
  • 36
  • 49
101V
  • 492
  • 1
  • 6
  • 13
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."

Jason Capriotti
  • 1,836
  • 2
  • 17
  • 33
  • 3
    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 @julio – Hooman Bahreini May 19 '18 at 06:36
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
   }
}
Tom Makin
  • 3,203
  • 23
  • 23
  • 3
    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 service – oleksa Jul 10 '17 at 10:54
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.

Xavier J
  • 4,326
  • 1
  • 14
  • 25
  • 1
    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. – JD. May 21 '14 at 19:26
  • @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. – Travis May 22 '14 at 01:20
  • 11
    I'd disagree with this. You might have some preconditions to check that, if not fulfilled, running the service is pointless. – julio.g Jan 30 '15 at 15:45
  • 2
    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](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." – Jason Capriotti Oct 16 '15 at 13:15
  • 3
    Topshelf DOES provide a way of shutting down in this way. See @julio.g's answer below – Tom Ferguson May 11 '16 at 16:21
  • 10
    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. – Gaz Jun 23 '16 at 10:13
-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()

Meysam Tolouee
  • 569
  • 3
  • 17