-
Notifications
You must be signed in to change notification settings - Fork 4.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Detecting the user or the system closing .NET Core console application #7120
Comments
Try using IApplicationLifetime.. http://shazwazza.com/post/aspnet-core-application-shutdown-events/ |
This seems to be an ASP.NET Core feature. Are you sure this can be used in a .NET Core console application? |
Sorry, my mistake, missed the ".NET Core Console only" part of your question. Isn't closing console window directly same as killing the process since console windows do not have message loops? Maybe, you may create an Kestrel server instance without injecting MVC and manage your requirements (the way I suggested above) through startup class.. |
@nicolasr75 have you considered using https://msdn.microsoft.com/en-us/library/system.appdomain.processexit(v=vs.110).aspx? |
@AlexGhiondea yes I considered that but according to |
@nicolasr75 we have brought back quite a few APIs. You can check http://apisof.net for a recent list of API availability. With the recent set of APIs we have brought back, Can you try that? |
Is this available in .NET Core 1.1? |
@nicolasr75 it is not in .NET Core 1.1... It is in .NET Core 1.2 though. |
Ok, I will set up a project with the .NET Core dev feed on a test PC after Christmas. |
@AlexGhiondea https://github.com/dotnet/core-setup/blob/master/README.md Trying to run dotnet new I was asked to install .NET SDK and was pointed to Programs and features now shows two things installed This is my project.json for a simple command line application:
My code:
Running dotnet restore works fine, dotnet build gives me The type or namespace name 'AppDomain' does not exist in the namespace 'System' (are you missing an assembly reference?) Any idea what I'm doing wrong? |
@nicolasr75 I think you are using .NET 1.1. If you want to use the newer APIs you need to update the version of `System.Runtime.Extensions' to a newer one (i.e. 4.4.0-beta-24903-02) that we publish on the dotnet.myget.org feed. |
Talk about a throwback. Cut my teeth on .NET 1.1 way back in 2003. |
I meant .NET Core 1.1 :). |
@AlexGhiondea Sorry to bother you again but I still cannot get this to run. My current project.json is like this:
.NET Core installations are still as shown in my post above. In my code I changed a single line:
I was missing the fact that we need to use the static CurrentDomain property. This restores and builds successfully. To have the dev feed I use a local nuget.config like this:
Furthermore notice that I use netcoreapp1.2 in project.json but Microsoft.NETCore.App 1.1.0. I'm not sure whether this is correct. Executing dotnet run fails with
I have no idea where this dependency comes from. Any further idea? |
@joperezr do you have any tips on how to figure out where that reference is coming from? |
@nicolasr75 unfortunately you will not be able to use the new APIs while referencing Microsoft.NETCore.App 1.1.0. You will need to reference a Microsoft.NETCore.App 1.2.0-beta in order to use the newer APIs. Also to reference that newer versions you will need to have a newer set of CLI tools at least preview 4 version of the tools. @terrajobst is working on writing up how to consume our newest prerelease packages. |
@weshaggard thanks for these informations. I updated everything as you say and also switched from project.json to msbuild. Finally it runs. Here is the final code:
The result is: I read that for Windows applications there exists a SetConsoleCtrlHandler API in kernel32. I have not yet tried that myself but from what others report this should do what I need. I had hoped that I don't need to pinvoke and that there were a cross-platform way. |
Good to hear that the code is running now. As for the behavior of ProcessExit I'm not sure. @rahku do you know what the expected behavior is for this case? |
I don't it will be invoked in case of rude termination of process. |
It looks like Linux does by default warn the user if there is still an active process running in a terminal. This is a great feature! Unfortunately Windows does not seem to support this :-( Maybe I could handle this by pinvoking SetConsoleCtrlHandler but in my case I will switch to a service application on Windows anyway which should give me better control. So from my side this issue could be closed. Thanks to all! |
@danmosemsft ProcessExit still isn't invoked when close a console window. How else can we detect the closing of a dotnet core console app? |
@Workshop2 I did this using System;
using System.Diagnostics;
namespace ConsoleApp4
{
class Program
{
static void Main(string[] args)
{
AppDomain.CurrentDomain.ProcessExit += (s, ev) =>
{
Console.WriteLine("process exit");
Console.ReadLine();
};
Console.CancelKeyPress += (s, ev) =>
{
Console.WriteLine("Ctrl+C pressed");
Console.ReadLine();
ev.Cancel = true;
};
Console.WriteLine("hit a key");
Console.ReadLine();
}
}
} And I get the same behavior on .NET Core 2.0 as on Desktop. This whether I hit Ctrl-C or I hit enter at the prompt. Can you give me a repro that behaves differently on Core? |
That behavior being, that output occurs thus:
|
Hello @danmosemsft, I am able to hook into https://github.com/noobot/noobot/blob/dotnet-standard-port/src/Noobot.Console/Program.cs#L21 |
@danmosemsft @Workshop2 I can confirm this. I just tried my test code from above (Jan 9th) with .NET Core 2.0 and it still behaves the same, no ProcessExit when closing the window. |
On version 2.0.2, I don't get the ctrl-c pressed message, does this fail for anyone else? |
I have same issue. .Net Core 2.0 and 2.0.3 app doing AppDomain.CurrentDomain.ProcessExit += ... will never fire on closing window by the X Console.CancelKeyPress works fine on both |
@jeffschw do you guys own this part? |
I just want to execute some cleanup before my program exits. I hope this issue gets resolved before .NET 5. |
Moving to 5.0 milestone just to do a quick check to see if there is anything that we want/need to do in here for 5.0 as from the discussion above it is not clear yet what works and what doesn't. |
Seems related to #36089 |
@ricardoboss: Yea, me too. @joperezr; |
I'm still using my above code (4 Nov 2018) with .NET Core 3.1. And after digging through lots of WebHost and other host code from the framework, I know that it's still used there as well. So there might not be a ready-to-use API for this, but the code shown above solves the problem completely. On Windows you have about 3 seconds before the OS kills the process. No way around that. On Linux with Systemd, you can configure this and it defaults to 90 seconds. When using the application shutdown behaviour that's built into the hosts (like WebHost), you can use additional configuration to prolong the internal default of 5 seconds before hosted services get aborted. This is another thing though and unrelated to what the OS does. |
From my testing, it looks like
Shutdown is not handled correctly - that's tracked by #36089. On Unix, it looks like closing a terminal window sends A few questions:
|
One datapoint might be what Mono does. |
Wasn't SIGHUP the signal that Systemd sends when a service should be reloaded? I have no experience with Linux desktop though, so never really use a terminal window, only things like PuTTY. |
@ygoe:
So, while many services will trigger a reload when receiving a SIGHUP, some might as well ignore the signal or terminate. (Source: https://unix.stackexchange.com/questions/239599/does-a-service-restart-send-a-hup) Actually, systemd will look into the ExecReload= option in the [Service] section in the .service unit file. From
(Source: https://superuser.com/questions/710986/how-to-reload-nginx-systemctl-or-nginx-s/953901#953901)
Also about
Use KDE Neon to get a nice Ubuntu 20.04 based Linux Desktop with KDE Plasma . ;) |
It looks to me like mono doesn't handle |
I tried this in .NET 5.0 but there's no WaitAsync() method on CancellationToken. Any advice how to get this running in .NET 5.0? |
@VILLAN3LL3 I've used C#'s |
@VILLAN3LL3 What are you trying to wait asynchronously for? There's nothing in the quoted code. Remember, you must not leave the ProcessExit event handler, not even by await, or the process will end immediately. The point is to block that method until the event has been set. |
@jnm2 Thank you very much for your advice. I had to additionally catch @ygoe Sorry I quoted the wrong part of your code. It's corrected now. |
@VILLAN3LL3 That Yes, that use of a CancellationToken throws an exception when cancelled. Also note that TaskCanceledException is derived from OperationCanceledException and I never know which of the two is actually thrown, so I always catch the OCE as the more generic one to be safe. If somebody has found a pattern behind these two, please let me know. |
@ygoe Roughly, |
Yes, but the caller of a method often doesn't know what implementation is in there, or doesn't want to rely on that detail. |
All a caller should care about is the base exception type, as far as I know. |
I believe the original issue has been addressed. Using .NET 6, you can subscribe to https://docs.microsoft.com/dotnet/api/system.appdomain.processexit, and it will be invoked even when the terminal window is closed. For handling I'm going to close this issue has I believe the original problem has been addressed. If you have a different problem, please log a new issue. |
Is there any way to detect whether a user or the system closes a console application? I support Ctrl-C for closing by a user but since the system provides the ability to simply close the console window I can not clean up my application which is important since it performs regular tasks that should be terminated cleanly. The same goes for system shutdown.
I tried
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading
but that does not fire when the window is closed.
The text was updated successfully, but these errors were encountered: