Reputation: 227
I have a dotnet core 2.2 console app.
I hosted it as windows service.
Service starts up another dotnet core WebAPI.
The problem is, how do I gracefully shutdown WebAPI process when the the service is stopped?
Note: I don't want to use Kill() method.
Sample code:
public class MyService : IHostedService, IDisposable
{
private Timer _timer;
static Process webAPI;
public Task StartAsync(CancellationToken cancellationToken)
{
_timer = new Timer(
(e) => StartChildProcess(),
null,
TimeSpan.Zero,
TimeSpan.FromMinutes(1));
return Task.CompletedTask;
}
public void StartChildProcess()
{
try
{
webAPI = new Process();
webAPI.StartInfo.UseShellExecute = false;
webAPI.StartInfo.FileName = @"C:\Project\bin\Debug\netcoreapp2.2\publish\WebAPI.exe";
webAPI.Start();
}
catch (Exception e)
{
// Handle exception
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
// TODO: Add code to stop child process safely
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
Upvotes: 1
Views: 3403
Reputation: 1060
Maybe check out MedallionShell. It's multi-platform and has a TrySignalAsync(CommandSignal.ControlC)
method that you can use to gracefully shut down another app. There are probably restrictions, but it works with ASP.Net Core apps that wait for a Ctrl + C
signal.
Example:
var command = Command.Run("dotnet", "run", "--project", "C:\MyRepo\MyProj\MyProj.csproj");
// Implementation details here
await command.TrySignalAsync(CommandSignal.ControlC);
Upvotes: 0
Reputation: 3905
Perhaps it's possible to use the CloseMainWindow
method of the System.Diagnostics.Process
class? I stumbled upon it when I was looking for a way to send a WM_CLOSE message to another process from C#.
Edit:
CloseMainWindow
does not seem to work if the main window is "blocked", for example by an open modal window or a dialog.
You might investigate a strategy for sending a WM_CLOSE or WM_DESTROY message to your app process explicitly. But I cannot guarantee if it will work as you want/expect. I haven't worked with that kind of Windows Messages functionality for a very long time.
Upvotes: 0
Reputation: 66
Technically you could simply call Process.Kill()
in order to immediately shut down the process. However, a lot of the time that is not the way to go simply because the WebAPI might be in middle of important operations and you can't really tell when those actions may be happening and Process.Kill()
is not really considered "graceful".
What would be most prudent to do is to tell the process that you would like for it to shut down at the earliest convenience and then allow for the WebAPI to clean things up before it exits itself. If you are desiging the WebAPI that is even better because that way you can decide on how to do this. Only calling Kill()
when it is absolutely necessary.
You can do that multiple ways of course. Some that come to mind are Sockets that are periodically checked and sending a CTRL+C input to the WebAPI.
public Task StopAsync(CancellationToken cancellationToken)
{
// send request to shut down
// wait for process to exit and free its resources
process.WaitForExit();
process.Close();
process.Dispose();
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
Of course if this is Async then it wouldn't make sense to wait for it to exit inside of the method so you would simply wait or check if it has exited outside of this method.
Upvotes: 1
Reputation: 10929
There were a lot of threads regarding this issue on github, consider post 7426.
The solution is found here: StartAndStopDotNetCoreApp, the sample code of the program.cs is:
using System;
using System.IO;
using System.Diagnostics;
namespace StartAndStopDotNetCoreApp
{
class Program
{
static void Main(string[] args)
{
string projectPath = @"C:\source\repos\StartAndStopDotNetCoreApp\WebApplication";
string outputPath = @"C:\Temp\WebApplication";
Console.WriteLine("Starting the app...");
var process = new Process();
process.StartInfo.WorkingDirectory = projectPath;
process.StartInfo.FileName = "dotnet";
process.StartInfo.Arguments = $"publish -o {outputPath}";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit();
process.Close();
process.Dispose();
process = new Process();
process.StartInfo.WorkingDirectory = outputPath;
process.StartInfo.FileName = "dotnet";
process.StartInfo.Arguments = $"{projectPath.Split(@"\")[projectPath.Split(@"\").Length - 1]}.dll";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
Console.WriteLine("Press anything to stop...");
Console.Read();
process.Kill();
}
}
}
If this is not what you are looking for, search the mentioned thread for more ways, it offers plenty.
Upvotes: 0