How should a service exit to prevent an error?

Andy Reynolds 30 Reputation points
2023-01-30T23:07:24.4+00:00

I'm developing a Windows service in C#, .NET 6.0 following the instructions on https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service The service runs as expected, but when I stop the service, it logs an error

A task was canceled. Exception: System.Threading.Tasks.TaskCanceledException: A task was canceled. at AWLService.WindowsBackgroundService.ExecuteAsync(CancellationToken stoppingToken) in C:\Minerva\Projects\AWLService_Test\WindowsBackgroundService.cs:line 22

The ExecuteAsync for this program is as follows:

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                string joke = _jokeService.GetJoke();
                _logger.LogWarning("{Joke}", joke);
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "{Message}", ex.Message);
            // Terminates this process and returns an exit code to the operating system.
            // This is required to avoid the 'BackgroundServiceExceptionBehavior', which
            // performs one of two scenarios:
            // 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
            // 2. When set to "StopHost": will cleanly stop the host, and log errors.
            //
            // In order for the Windows Service Management system to leverage configured
            // recovery options, we need to terminate the process with a non-zero exit code.
            Environment.Exit(1);
        }
    }

I've tried catching TaskCanceledException. When I do that and add Environment.Exit(0), I still get the error. If I take out Environment.Exit, the service terminates without an error, but it logs "Service stopped successfully" twice.

What is the proper way I should be ending my service?

Thank you.

Developer technologies .NET Other
Developer technologies C#
0 comments No comments
{count} vote

1 answer

Sort by: Most helpful
  1. Alan Farias 755 Reputation points
    2023-02-24T15:23:32.59+00:00

    In a Windows service, when you stop the service, a cancellation token is generated, and the service's ExecuteAsync method will be called with that token. In your code, you have implemented a while loop to run the service. When the cancellation token is requested, the loop will exit and the ExecuteAsync method will be terminated.

    The error you are seeing is caused by the TaskCanceledException being thrown when the loop exits. This exception is expected, and you do not need to catch it. Instead, you can use a try-catch block to handle any other exceptions that may be thrown while running the service.

    Here's an updated ExecuteAsync method that avoids the error and properly handles exceptions:

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                string joke = _jokeService.GetJoke();
                _logger.LogWarning("{Joke}", joke);
                await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
            }
        }
        catch (OperationCanceledException)
        {
            // This exception is expected when the service is stopped.
            // Do nothing here.
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "{Message}", ex.Message);
            // Handle other exceptions here.
        }
    }
    

    With this code, when the service is stopped, the OperationCanceledException will be caught and the service will exit cleanly without logging an error. If there is another exception, it will be caught and logged by the _logger, and the service will exit with a non-zero exit code, indicating an error.

    1 person found this answer helpful.
    0 comments No comments

Your answer

Answers can be marked as Accepted Answers by the question author, which helps users to know the answer solved the author's problem.