5

I am following this tutorial: http://msdn.microsoft.com/en-us/library/zt39148a(v=vs.110).aspx to create a windows service. I have a class called TaskManager which uses Quartz.Net to manage a bunch of jobs. It has .Go() (which doesn't block) and .Stop() methods. If I've understood correctly, all I need to do in my service is

    private TaskManager _taskManager;

    public DataPumpService()
    {
        InitializeComponent();
        _taskManager = new TaskManager();
    }

    protected override void OnStart(string[] args)
    {
        _taskManager.Go();
    }

    protected override void OnStop()
    {
        _taskManager.Stop();
    }

But then the tutorial has a section on setting the service status. It doesn't really explain what the service status is or when I would want to set it. TaskManager.Stop() can take a few seconds to finish (internally it call IScheduler.Interrupt() on all jobs and then IScheduler.Shutdown(true)). So should I be setting statuses? If so then assuming I include the code in sections (1), (2) and (3) from the Setting Service Status section of the tutorial, is it correct to do the following (essentially for both methods in my first code block above):

    protected override void OnStop()
    {
        // Update the service state to Stop Pending.
        ServiceStatus serviceStatus = new ServiceStatus();
        serviceStatus.dwCurrentState = ServiceState.SERVICE_STOP_PENDING;
        serviceStatus.dwWaitHint = 100000;
        SetServiceStatus(this.ServiceHandle, ref serviceStatus);

        _taskManager.Stop();

        // Update the service state to Running.
        serviceStatus.dwCurrentState = ServiceState.SERVICE_STOPPED;
        SetServiceStatus(this.ServiceHandle, ref serviceStatus);
    }

If this is right, then is the serviceStatus.dwWaitHint = 100000; property something I need to choose wisely or is that default value a good thing to stick with? Essentially I have no idea what this value is for...

Dan
  • 45,079
  • 17
  • 88
  • 157
  • 1
    Hmm, not sure how that made it into that MSDN article. The way the ServiceBase class already takes care of the service status is good enough in 99.9% of the cases. Including this dwWaitHint, it is wrapped by the ServiceBase.RequestAdditionalTime() method. You should not need it, 30 seconds is good enough to get 99.9% of all services started :) Note that none of the versions of this article prior to 4.5 talk about it at all. – Hans Passant Oct 07 '14 at 20:11
  • @HansPassant so are you saying that if I just use my first block of code and don't mess with the serviceStatus at all - it is generally fine? Also what do you mean by 30 seconds... is that a default or are you suggesting I use 30000 instead of 100000? – Dan Oct 07 '14 at 21:29
  • 2
    Don't mess with it. 30 seconds is the default timeout that Windows uses. – Hans Passant Oct 07 '14 at 21:32
  • 1
    Thanks! Please add as an asnwer – Dan Oct 08 '14 at 05:56
  • I'd also strongly suggest checking out the Topshelf project that makes the whole windows services story a whole lot easier http://topshelf-project.com/ . Quartz.NET also includes a sample of this in the Quartz.Server project. – Marko Lahma Oct 09 '14 at 18:46

1 Answers1

8

As @HansPassant says

The way the ServiceBase class already takes care of the service status is good enough in 99.9% of the cases. You should not need it, 30 seconds (the default) is good enough to get 99.9% of all services started/stopped.

But if you need to handle a long running close, the documentation says concerning

dwWaitHint

The estimated time required for a pending start, stop, pause, or continue operation, in milliseconds. Before the specified amount of time has elapsed, the service should make its next call to the SetServiceStatus function with either an incremented dwCheckPoint value or a change in dwCurrentState. If the amount of time specified by dwWaitHint passes, and dwCheckPoint has not been incremented or dwCurrentState has not changed, the service control manager or service control program can assume that an error has occurred and the service should be stopped. However, if the service shares a process with other services, the service control manager cannot terminate the service application because it would have to terminate the other services sharing the process as well.

and

dwCheckPoint

The check-point value the service increments periodically to report its progress during a lengthy start, stop, pause, or continue operation. For example, the service should increment this value as it completes each step of its initialization when it is starting up. The user interface program that invoked the operation on the service uses this value to track the progress of the service during a lengthy operation. This value is not valid and should be zero when the service does not have a start, stop, pause, or continue operation pending.

This clarifies the footnote in the walk-through.

The Service Control Manager uses the dwWaitHint and dwCheckpoint members of the SERVICE_STATUS structure to determine how much time to wait for a Windows Service to start or shut down. If your OnStart and OnStop methods run long, your service can request more time by calling SetServiceStatus again with an incremented dwCheckPoint value.

Based on that, I wrote my Stop code like such. Note that, I run some very long tasks and terminating them is really not a good idea hence the lengthy wait times.

//set the status to pending close
var serviceStatus = new ServiceStatus
{
    dwCurrentState = ServiceState.SERVICE_STOP_PENDING,
    dwWaitHint = 120000//two minutes wait time
};
SetServiceStatus(this.ServiceHandle, ref serviceStatus);

Engine.Cancel();

while (Engine.IsRunning())
{
    System.Threading.Thread.Sleep(1000);
    serviceStatus.dwCheckPoint++;//updating the checkpoint so I don't get terminated
    SetServiceStatus(this.ServiceHandle, ref serviceStatus);
}
Community
  • 1
  • 1
frostymarvelous
  • 2,786
  • 32
  • 43
  • 1
    Thanks, if you add a section to the top of this answer that specifies what Hans Passant said in the comments, i.e. in 99.9% of the cases you should just leave the defaults, I'll be happy to accept this. – Dan Jul 20 '16 at 07:10
  • Added it. Thanks. It actually makes it a way better. – frostymarvelous Jul 20 '16 at 11:07