Search code examples
c#serviceninjecttopshelf

How to handle service session changes with TopShelf


After a week of research and trying to get session changes handled I still have not been able to get the results I'm looking for. I've looked at all available examples and thoroughly examined the TopShelf source code. My main class is as follows:

class Program 
{
    static void Main(string[] args)
    {
        //Always start out in our apps base directory
        Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory);
        const string serviceName = "Service";
        const string displayName = "Service";
        const string description = "Provide assistance";
        try
        {               
            HostFactory.Run(x =>
            {                   
                x.UseCommonLogging();
                x.UseNinject(new IocModule());                    
                x.Service<ServService.Service.WinService>(sc =>
                {                        
                    sc.WhenStarted((s, hostControl) => s.Start(hostControl));
                    sc.WhenStopped((s, hostControl) => s.Stop(hostControl));
                    sc.WhenSessionChanged((se, e, id) =>
                    {
                        se.SessionChange(e, id);
                    });
                    //sc.WhenSessionChanged((s, chg) => s.SessionChange(chg));
                    sc.ConstructUsingNinject();
                });
                x.EnableSessionChanged();
                x.RunAsLocalSystem();


                x.EnableServiceRecovery(r =>
                {
                    r.RestartService(0);

                });
                x.StartAutomatically();                    
                x.SetDescription(description);
                x.SetDisplayName(displayName);
                x.SetServiceName(serviceName);

And here is my Service class:

{

class WinService : ServiceControl 
{
    private CancellationTokenSource cancelSource;
    private CancellationToken ct;
    public ILog Log { get; private set; }

    public WinService(ILog logger)
    {
        if (logger == null)
            throw new ArgumentNullException(nameof(logger));

    }

    public void SessionChange(SessionChangedArguments chg)
    {
        Log.Info("Service session changed!!!!!!!!!!!");
    }

    //Starts service
    public bool Start(HostControl hostControl)
    {
        Console.Writeline("STARTED!);
        return true;
    }

    //Stops service
    public bool Stop(HostControl hostControl)
    {
        cancelSource.Cancel();
        return true;
    }

}

Everytime I run the code and regardless of any changes I make the default values from the TopShelf source code API WindowsServiceHost.csclass are printed instead which is

"[Topshelf] Service session changed"

but according to my code it should print "Service session changed!!!!!!!!!!!" isntead. Here is the Top Shelf Source Code that I'm referring to and the relevant piece starts on line 217. Any help greatly appreciated as always.


Solution

  • An old question, but I was able to get my events to fire using a my IMyService class (and concrete) that is "standalone", aka, does not inherit from any service base from microsoft or topshelf

    public interface IMyServiceContract
    {
        void Start();
    
        void Stop();
    
        void SessionChanged(Topshelf.SessionChangedArguments args);
    }
    

    My concrete:

    public class MyService : IMyServiceContract
    {
    
        public void Start()
        {
        }
    
        public void Stop()
        {
    
        }
    
        public void SessionChanged(SessionChangedArguments e)
        {
            Console.WriteLine(e.ReasonCode);
        }   
    
    }
    

    and my "program.cs "main"" method

                IMyServiceContract myServiceObject = new MyService(); // // container.Resolve<IMyServiceContract>();
    
    
                HostFactory.Run(x =>
                {
                    x.Service<IMyServiceContract>(s =>
                    {
                        s.ConstructUsing(name => myServiceObject);
                        s.WhenStarted(myso => myso.Start());
                        s.WhenStopped(myso => myso.Stop());
                        s.WhenSessionChanged((myso, hc, chg) => myso.SessionChanged(chg));
                    });
    
                    x.EnableSessionChanged();
    

    If you write to a text file (where i have Console.WriteLine(e.ReasonCode);)...you can see the changes. I tested by doing a LockUser (in windows 10 x64) and logging back in.

    It's working for me.

    My packages.config so you know which version of TopShelf I'm using

      <package id="log4net" version="2.0.5" targetFramework="net45" />
      <package id="Topshelf" version="4.0.3" targetFramework="net461" />
      <package id="Topshelf.Log4Net" version="4.0.3" targetFramework="net461" />