Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to choose value for serviceStatus.dwWaitHint?

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...

like image 289
Dan Avatar asked Oct 07 '14 17:10

Dan


1 Answers

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);
}
like image 155
frostymarvelous Avatar answered Nov 18 '22 15:11

frostymarvelous