Getting a Topshelf service to run once on boot, then stop
Asked Answered
N

1

5

I have written a TopShelf service / console app, which appears to be running as intended, except I would like it to run once on boot, then disable itself until the next boot/reboot.

I was hoping this would work:

class MyServiceClass
{
    public void Start()
    {
        // do the things that need doing

        this.Stop();
    }

    public void Stop()
    {
    }

But that doesn't work, presumably because the this.Stop() command is there for cleanup, not for causing the service to stop.

My Program.cs looks like this:

// Uses Topshelf: http://topshelf-project.com/
// Guided by: http://www.ordina.nl/nl-nl/blogs/2013/maart/building-windows-services-with-c-and-topshelf/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

using Topshelf;

namespace MyNamespace
{
    class Program
    {
        static void Main(string[] args)
        {
            HostFactory.Run(hostConfigurator =>
            {
                hostConfigurator.Service<MyServiceClass>(serviceConfigurator =>
                {
                    serviceConfigurator.ConstructUsing(() => new MyServiceClass());
                    serviceConfigurator.WhenStarted(myServiceClass => myServiceClass.Start());
                    serviceConfigurator.WhenStopped(myServiceClass => myServiceClass.Stop());
                });

                hostConfigurator.RunAsLocalSystem();

                hostConfigurator.SetDisplayName("MyService");
                hostConfigurator.SetDescription("Does stuff.");
                hostConfigurator.SetServiceName("MyService");
                hostConfigurator.StartAutomatically();
                hostConfigurator.EnableShutdown();
            });
        }
    };
}

How do I go about stopping the service at the end of execution?

UPDATE: Based on Damien's input, I now have:

public class MyServiceClass
{
    private readonly Task task;
    private HostControl hostControl;

    public MyServiceClass()
    {
        task = new Task(DoWork);
    }

    private void DoWork()
    {
        Console.WriteLine("Listen very carefully, I shall say this only once");
        hostControl.Stop();
    }

    public void Start(HostControl hostControl)
    {
        // so we can stop the service at the end of the check
        this.hostControl = hostControl;

        // start the DoWork thread
        task.Start();
    }

    public void Stop()
    {

    }

};

and an updated Program.cs

class Program
{
    static void Main(string[] args)
    {

        HostFactory.Run(hostConfigurator =>
        {
            hostConfigurator.Service<MyServiceClass>((serviceConfigurator =>
            {
                serviceConfigurator.ConstructUsing(() => new MyServiceClass());
                serviceConfigurator.WhenStarted((myServiceClass, hostControl) => myServiceClass.Start(hostControl));
                serviceConfigurator.WhenStopped(myServiceClass => myServiceClass.Stop());
            });    /* compiler thinks there's a ")" missing from this line */

            hostConfigurator.RunAsLocalSystem();

            hostConfigurator.SetDisplayName("MyService");
            hostConfigurator.SetDescription("Does stuff.");
            hostConfigurator.SetServiceName("MyService");
            hostConfigurator.StartAutomatically();
            hostConfigurator.EnableShutdown();
        });

    }
};

However, this will not compile. My compiler suggests that a ")" is missing on (or around) my comment (shown in the code above), but any addition of a close parenthesis just adds to the error list.

I feel like I'm close... any ideas?

Nereid answered 30/4, 2014 at 14:39 Comment(5)
Maybe check the documentation: "Each of the WhenXxx methods can also take an argument of the HostControl interface ... The HostControl interface can be retained and used as the service is running to Stop the service." (Although you probably need to move your //do the things that need doing and using the HostControl interface out into a separate thread because I think you have to have returned from your Start method before you attempt to stop the service)Collins
Thanks Damien. I'll post my code once it's working.Nereid
@Collins - I am updating the question to reflect my attempt to implement use of HostControl, and would appreciate your (and everyone else's) input.Nereid
Additionally, pre-compile, intellisense says "Error: Delegate 'System.Func<MyNamespace.MyServiceClass>' does not take one argument.Nereid
note i think you are missing a ')' - you have one to many opening bracket in in the line reading `` ((serviceConfigurator =>``Rubberneck
N
8

Got this working eventually:

public class MyServiceClass : ServiceControl
{
    private readonly Task task;
    private HostControl hostControl;

    public MyServiceClass()
    {
        task = new Task(DoWork);
    }

    private void DoWork()
    {
        Console.WriteLine("Listen very carefully, I shall say this only once");
        //hostControl.Stop();
    }

    public bool Start(HostControl hostControl)
    {
        // so we can stop the service at the end of the check
        this.hostControl = hostControl;

        // start the DoWork thread
        task.Start();

        return true;
    }

    public bool Stop(HostControl hostControl)
    {
        return true;
    }

};

and

class Program
{
    static void Main(string[] args)
    {

        HostFactory.Run(hostConfigurator =>
        {
            hostConfigurator.Service<MyServiceClass>(serviceConfigurator =>
            {
                serviceConfigurator.ConstructUsing(() => new MyServiceClass());
                serviceConfigurator.WhenStarted((myServiceClass, hostControl) => myServiceClass.Start(hostControl));
                serviceConfigurator.WhenStopped((myServiceClass, hostControl) => myServiceClass.Stop(hostControl));
            });

            hostConfigurator.RunAsLocalSystem();

            hostConfigurator.SetDisplayName("MyService");
            hostConfigurator.SetDescription("Does stuff.");
            hostConfigurator.SetServiceName("MyService");
            hostConfigurator.StartAutomatically();
            hostConfigurator.EnableShutdown();
        });

    }
};

My 2nd attempt had a spare "(" at the first mention of serviceConfigurator, then I needed to turn my void Start and Stop functions into bool functions. Hope this helps someone.

Nereid answered 8/5, 2014 at 15:19 Comment(1)
For this to work, you need to uncomment hostControl.Stop();Bargainbasement

© 2022 - 2024 — McMap. All rights reserved.