Gracefully exit a .net 8 based windows service [SOLVED]
-
It seems that the .NET 8 infrastructure code that interacts with the service control manager provides a layer of abstraction over the Start and Stop commands. Therefore I don't see how to directly issue a service stop command. The service is passed a CancellationToken object, but I've been informed that you can't cancel such a token without access to the CancellationTokenSource. When the service runs, all I have is the token, not the source. So how do I shutdown and exit the service from code running inside the service? SOLUTION: Below is the template code produced by Visual Studio when you create a .NET 8 Windows service: I simply created a property on the service class itself that can be set from inside the service and indicates to the outside world whether the service wants to shut down. I called it
StopFlag
.public sealed class WindowsBackgroundService( MyWindowsService myWindowsService, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { myWindowsService.OnStart(new string\[0\]); while (!stoppingToken.IsCancellationRequested && !myWindowsService.StopFlag) { await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); } } catch (OperationCanceledException) { // When the stopping token is canceled, for example, a call made from services.msc, // we shouldn't exit with a non-zero exit code. In other words, this is expected... } 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); } }
-
It seems that the .NET 8 infrastructure code that interacts with the service control manager provides a layer of abstraction over the Start and Stop commands. Therefore I don't see how to directly issue a service stop command. The service is passed a CancellationToken object, but I've been informed that you can't cancel such a token without access to the CancellationTokenSource. When the service runs, all I have is the token, not the source. So how do I shutdown and exit the service from code running inside the service? SOLUTION: Below is the template code produced by Visual Studio when you create a .NET 8 Windows service: I simply created a property on the service class itself that can be set from inside the service and indicates to the outside world whether the service wants to shut down. I called it
StopFlag
.public sealed class WindowsBackgroundService( MyWindowsService myWindowsService, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { myWindowsService.OnStart(new string\[0\]); while (!stoppingToken.IsCancellationRequested && !myWindowsService.StopFlag) { await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); } } catch (OperationCanceledException) { // When the stopping token is canceled, for example, a call made from services.msc, // we shouldn't exit with a non-zero exit code. In other words, this is expected... } 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); } }
Don't you just call the ServiceBase.Stop Method (System.ServiceProcess) | Microsoft Learn[^] ?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony "Common sense is so rare these days, it should be classified as a super power" - Random T-shirt AntiTwitter: @DalekDave is now a follower!
-
Don't you just call the ServiceBase.Stop Method (System.ServiceProcess) | Microsoft Learn[^] ?
"I have no idea what I did, but I'm taking full credit for it." - ThisOldTony "Common sense is so rare these days, it should be classified as a super power" - Random T-shirt AntiTwitter: @DalekDave is now a follower!
Griff, thanks for the reply. The method and object you mention are only in the .NET Framework. I recently upgraded the service to .NET 8, which uses a different paradigm.
The difficult we do right away... ...the impossible takes slightly longer.
-
It seems that the .NET 8 infrastructure code that interacts with the service control manager provides a layer of abstraction over the Start and Stop commands. Therefore I don't see how to directly issue a service stop command. The service is passed a CancellationToken object, but I've been informed that you can't cancel such a token without access to the CancellationTokenSource. When the service runs, all I have is the token, not the source. So how do I shutdown and exit the service from code running inside the service? SOLUTION: Below is the template code produced by Visual Studio when you create a .NET 8 Windows service: I simply created a property on the service class itself that can be set from inside the service and indicates to the outside world whether the service wants to shut down. I called it
StopFlag
.public sealed class WindowsBackgroundService( MyWindowsService myWindowsService, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { myWindowsService.OnStart(new string\[0\]); while (!stoppingToken.IsCancellationRequested && !myWindowsService.StopFlag) { await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); } } catch (OperationCanceledException) { // When the stopping token is canceled, for example, a call made from services.msc, // we shouldn't exit with a non-zero exit code. In other words, this is expected... } 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); } }
Store a "semaphore" (or "command code") somewhere and have the service look at it every so often. Use pull instead of push, and let it shut itself down.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
-
It seems that the .NET 8 infrastructure code that interacts with the service control manager provides a layer of abstraction over the Start and Stop commands. Therefore I don't see how to directly issue a service stop command. The service is passed a CancellationToken object, but I've been informed that you can't cancel such a token without access to the CancellationTokenSource. When the service runs, all I have is the token, not the source. So how do I shutdown and exit the service from code running inside the service? SOLUTION: Below is the template code produced by Visual Studio when you create a .NET 8 Windows service: I simply created a property on the service class itself that can be set from inside the service and indicates to the outside world whether the service wants to shut down. I called it
StopFlag
.public sealed class WindowsBackgroundService( MyWindowsService myWindowsService, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { myWindowsService.OnStart(new string\[0\]); while (!stoppingToken.IsCancellationRequested && !myWindowsService.StopFlag) { await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); } } catch (OperationCanceledException) { // When the stopping token is canceled, for example, a call made from services.msc, // we shouldn't exit with a non-zero exit code. In other words, this is expected... } 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); } }
-
It seems that the .NET 8 infrastructure code that interacts with the service control manager provides a layer of abstraction over the Start and Stop commands. Therefore I don't see how to directly issue a service stop command. The service is passed a CancellationToken object, but I've been informed that you can't cancel such a token without access to the CancellationTokenSource. When the service runs, all I have is the token, not the source. So how do I shutdown and exit the service from code running inside the service? SOLUTION: Below is the template code produced by Visual Studio when you create a .NET 8 Windows service: I simply created a property on the service class itself that can be set from inside the service and indicates to the outside world whether the service wants to shut down. I called it
StopFlag
.public sealed class WindowsBackgroundService( MyWindowsService myWindowsService, ILogger logger) : BackgroundService { protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { myWindowsService.OnStart(new string\[0\]); while (!stoppingToken.IsCancellationRequested && !myWindowsService.StopFlag) { await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); } } catch (OperationCanceledException) { // When the stopping token is canceled, for example, a call made from services.msc, // we shouldn't exit with a non-zero exit code. In other words, this is expected... } 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); } }
So.... Environment.Exit([exit_code]); ?? Not on this yet, but soon enough. Real soon, if I have it my way. I think it does that abstraction to help with the way it facades for running services in local debug without them actually being services? It seems like we DIY'd that into the old stuff.