How to re-start a console application if it crashes? - c#

I have created a console application in C#. How can I program this application so that it will re-start itself after a crash?

If I understand your question correctly, you want to attempt to re-start a console app in the event of a crash. In C# console-apps the method defined as the entry point (usually static void main) is the root of the call stacks in the app. You essentially would need to call that method recursively. You will want to make sure that the app eventually fails if it is in some unintended or unrecoverable state.
For example in the main class:
static int retryCount;
const int numberOfRetries = 3;
static void Main(string[] args)
{
try
{
var theApp = new MyApplicationType(args);
theApp.StartMyAppLogic();
}
catch (ExpectedExceptionType expectThisTypeOfException)
{
thisMethodHandlesExceptions(expectThisTypeOfException);
}
catch (AnotherExpectedExceptionType alsoExpectThisTypeOfException)
{
thisMethodHandlesExceptions(alsoExpectThisTypeOfException);
}
catch (Exception unexpectedException)
{
if(retryCount < numberOfRetries)
{
retryCount++;
Main(args);
}
else
{
throw;
}
}
}

You can use a watchdog to process your monitor and restart it if crashed:
see: What's the best way to watchdog a desktop application?
You can use a windows service instead and set it's recovery options as indicated here: https://serverfault.com/questions/48600/how-can-i-automatically-restart-a-windows-service-if-it-crashes
You can use a scheduled task in task manager to start your application periodically , and set it to only start if previous run has ended:
https://support.microsoft.com/en-us/kb/323527

You could try something like this:
static void Main(string[] args)
{
try
{
// Application code goes here
}
catch (Exception)
{
var applicationPath = System.Reflection.Assembly.GetExecutingAssembly().Location;
Process.Start(applicationPath);
Environment.Exit(Environment.ExitCode);
}
}
Basically, wrap all the code in a try/catch, and if any exceptions occur, the program will retrieve the .exe location with System.Reflection.Assembly.GetExecutingAssembly().Location; and then call Process.Start to run the application again.

You should control your console app from another application (watchdog, sheduler, procmon, servman, ...).
E.g. you can create your console app as a service and control it from service manager.

Related

Why does .NetFramework console exe program shut down by itself after 1 day idle?

I have a Console application that is ran on the background. Its a simple server that built using NetMQ library and NLogger that works locally.
I ran the program using taskScheduler with the following command schtasks /create /sc ONSTART /tn "TheServer" /tr "C:\\Temp\TheServer.exe" /ru System
As you can se, now it will start every time the desktop is turned on and it will run as System (It means that we can see TheServer.exe is running as background through Task Manager)
Question:
My timeline was like the follows:
My PC was on on Friday morning (starting of Business day - 09:00 in the morning)
I didn't turn off my PC on Friday before I go back from work
Monday morning, I checked my PC and TheServer is already gone
Does anyone know why it happens and how to prevent it?
FYI, I write my code as follows
Program.cs
class Program
{
[STAThread]
static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
...
static void Main(string[] args)
{
try
{
var inst = Server.GetInstance();
inst.Bind();
inst.Run();
}
catch (Exception e)
{
log.Error(e.Message);
log.Error(e.StackTrace);
throw;
}
}
}
Server.cs
class Server
{
private NetMQ.Sockets.ResponseSocket rSocket = new NetMQ.Sockets.ResponseSocket();
static readonly NLog.Logger log = NLog.LogManager.GetCurrentClassLogger();
...
public void Bind()
{
try
{
//bind is only for the Responder or The Server
rSocket.Bind("tcp://127.0.0.1:32325");
}
catch (Exception e)
{
log.Error(e.Message);
log.Error(e.StackTrace);
throw;
}
}
public void Run()
{
while (true)
{
var message = server.ReceiveFrameString();
Console.WriteLine("Received {0}", message);
// processing the request
Thread.Sleep(100);
Console.WriteLine("Sending World");
server.SendFrame("World");
}
}
}
I wrap my code at Program.cs with try-catch. But nothing written in the log
Then
I tried to find some information at the Window's Event Viewer. But there are no related information why TheServer.exe stopped
Update:
Looks like I found what #PMF means.
Then one extra question. . .
Is there any way to do that through command prompt?
I tried to read https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/schtasks-create Looks like if I didn't set the /du when create the task, it will be forever (?). CMIIW
UPDATE
So yes, we decided to recreate this TheServer.exe as Windows Service. Thanks for your input and opinions.

Is there a way to force an AssemblyLoadContext to unload?

I am working on a big ASP.Net 5 web application, and I would like to implement a self-update feature. Since the program can be deployed on many different platform and different ways (e.g. a Windows Service, a systemd service on linux, Docker container, etc...), the only viable way of implementing a self update mechanism is to write a host process that can load and unload the main program DLL and its dependencies. Unloading is important because the host program (updater) must be able to overwrite the server's DLL files while it's running.
Here's what I've done so far: I have changed the output type of the main web application to Library, and I've made a small loader program that loads this DLL and its dependencies into an HostAssemblyLoadContext, then invokes the original Main() method. The server application is programmed to shut down gracefully a few seconds after it starts up, so the CreateHostBuilder(args).Build().Run() and the Main() method can return. After this, I try to call HostAssemblyLoadContext.Unload(), but for some reason, the load context refuses to actually unload.
Here's my HostAssemblyLoadContext implementation:
public class HostAssemblyLoadContext : AssemblyLoadContext
{
private readonly AssemblyDependencyResolver _resolver;
public HostAssemblyLoadContext(string pluginPath) : base(isCollectible: true)
{
_resolver = new AssemblyDependencyResolver(pluginPath);
}
protected override Assembly? Load(AssemblyName name)
{
string? assemblyPath = _resolver.ResolveAssemblyToPath(name);
if (assemblyPath != null)
return LoadFromAssemblyPath(assemblyPath);
string filePath = $"{name.FullName.Split(',')[0]}.dll";
if(File.Exists(filePath))
return Assembly.LoadFrom(filePath);
return null;
}
}
My loader code:
[MethodImpl(MethodImplOptions.NoInlining)]
private static void ExecuteMainMethod(string[] args, string assemblyFileName, out WeakReference contextReference, out HostAssemblyLoadContext loadContext)
{
loadContext = new HostAssemblyLoadContext(new DirectoryInfo(".").FullName);
contextReference = new WeakReference(loadContext);
var assembly = loadContext.LoadFromAssemblyPath(assemblyFileName);
var mainMethod = FindMainMethod(assembly)!;
try
{
mainMethod.Invoke(null, new object?[] {args});
}
catch
{
// ignore
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
private static void Unload(WeakReference weakReference, ref HostAssemblyLoadContext loadContext)
{
loadContext.Unload();
loadContext = null;
for (int i = 0; weakReference.IsAlive && i < 100; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
}
Console.WriteLine($"is alive: {weakReference.IsAlive}");
}
public static void Main(string[] args)
{
ExecuteMainMethod(args, new FileInfo("FirestormSW.SmartGrade.dll").FullName, out var weakReference, out var loadContext);
Unload(weakReference, ref loadContext);
Console.Out.WriteLine("Press ENTER to exit");
Console.ReadLine();
}
And the main method of the actual ASP server:
public static void Main(string[] args)
{
Console.Out.WriteLine("Starting Server...");
CreateHostBuilder(args).Build().Run();
Console.Out.WriteLine("Server stopped.");
}
Note that if I replace the contents of this Main method with a simple Thread.Sleep(1000), the context does unload successfully.
When I run this program, and wait for the server to shut itself down, this is what I see on the console:
Starting Server...
Shutting down...
is alive: True
This means that the server's main method has returned, but something is still keeping the load context alive. I have looked at the thread count (Process.GetCurrentProcess().Threads.Count) before and after the server is started/stopped, and the number jumps from 8 up to 27. This makes me assume that the context is being kept alive by some threads that are created by the ASP.Net application, but I'm not sure. And if that's the case, I don't know how to find out which threads are responsible, and even if I could, I'm not sure if it's possible to abort them.

C# Pause one console application when other console application starts

I have one console application for testing purposes like following:
static void Main(string[] args)
{
do
{
for (int i = 0; i < 10000000; i++)
{
Console.WriteLine("Doing some endless loop");
Console.WriteLine(i.ToString());
}
} while (true);
}
As you can see the code is very basic, and I've set it up to endless loop in order to test what I would like to achieve.
The other console application is called "Updater" and I would like to to pause the "EndlessLoop" console application once the "Updater" application is started.
Does anyone knows if this is doable in c# .NET?
public static bool IsAppRunning()
{
foreach (Process process in Process.GetProcesses())
{
if (process.ProcessName.Contains("Updater"))
{
return true;
}
}
return false;
}
If you call this in while loop it tells you if Updater is running or not.
Not easy to communicate between 2 application
One proposition: When your console Updater starts, you create a file in folder C:\Temps\token.txt. Then, if your console EndlessLoop detects a file names token.txt in C:\Temps, you pause EndlessLoop

windows service will not stop when I select stop from the services windows C#

I have written a windows service but when I try to stop the service it says that the service cannot be stopped at this time. Here's my whole class:
public partial class RenewalsService : ServiceBase
{
private readonly ManualResetEvent _shutdownEvent = new ManualResetEvent(false);
private Thread _thread;
public RenewalsService()
{
InitializeComponent();
this.CanStop = true;
}
protected override void OnStart(string[] args)
{
_thread = new Thread(WorkerThread)
{
Name = "Renewals Service Thread",
IsBackground = true
};
_thread.Start();
}
protected override void OnStop()
{
try
{
if (!_shutdownEvent.SafeWaitHandle.IsClosed)
{
_shutdownEvent.Set();
}
if (_thread.IsAlive)
{
if (!_thread.Join(3000))
{
// give the thread 3 seconds to stop
_thread.Abort();
}
}
}
catch (Exception ex)
{
// _thread.Join may raise an error at this point. If it does we dont care. We dont care about any other exceptions
// since we are already in the process of closing the service.
}
finally
{
IError logger = new Logger();
Exception ex = new Exception("The Renewals service has been stopped.");
logger.Log(this, SeverityEnum.Warning, ex);
Environment.ExitCode = 0;
Environment.Exit(Environment.ExitCode);
}
}
private void WorkerThread()
{
try
{
while (!_shutdownEvent.WaitOne(1))
{
string timeToRun = ConfigurationManager.AppSettings["RunTime"];
string[] timeStrings = timeToRun.Split(':');
TimeSpan runtime = new TimeSpan(0, Int32.Parse(timeStrings[0]), Int32.Parse(timeStrings[1]), Int32.Parse(timeStrings[2]));
if (DateTime.Today.TimeOfDay.Hours == runtime.Hours &&
DateTime.Today.TimeOfDay.Minutes == runtime.Minutes)
{
Renewals renewals = new Renewals();
renewals.GenerateRenewal();
}
}
}
catch (Exception ex)
{
IError logger = new Logger();
logger.Log(this, SeverityEnum.Warning, ex);
this.OnStop();
}
}
}
What's missing to ensure the user can stop the service.
Your code looks ok to me, so here's a couple of things to check.
First, does the GenerateRenewal() method take a long time to complete? If so, you might need to periodically check _shutdownEvent inside that method for a timely shutdown. Of course, you've marked the thread as a background thread so it should shut down when you tell the service to stop anyway. I haven't seen background threads hold up process termination, but I guess there's always that chance.
Second, the more likely culprit to me is that the service has already shut down due to an exception. The Services console doesn't automatically refresh when a service shuts down, so it's possible you see the Stop link available to you when it shouldn't be. If you hit F5, the console will refresh, and if your service has stopped, the Start link should be the only one available. Check your log files to see if your exception handlers have been triggered.
UPDATE
So it looks like your WorkerThread() method is throwing an exception, which causes the service to stop. This explains why the Stop link is giving you the error message when you click it.
Providing you have sufficient permissions on your box, use this link to debug your service to find out why the exception is occurring.
HTH
The base ServiceBase class calls your overridden virtual method OnStop() when the Windows Service Control Manager ("the SCM") has sent the service a "Stop" command. In the method's implementation you are supposed to do whatever is necessary to get your service to a stopped state, then return from the method back to the ServiceBase class, which handles the interaction with the SCM, in this case to tell the SCM that your service is now stopped. The SCM will decide when your service process should be terminated, and the ServiceBase class handles that without you needing to do anything explicit.
For a well-behaved service, you should either just return at the end of your OnStop method, or throw an exception. The ServiceBase class will handle things appropriately, including logging your exception, if you have thrown one, as an error in the Windows Event Log. If your method may take a while to get your service stopped, you should call base.RequestAdditionalTime() at the appropriate points, so the base class can tell the SCM that you haven't just hung, your service is in the process of stopping.
I think your main problem lies in these lines:
Environment.ExitCode = 0;
Environment.Exit(Environment.ExitCode);
You never return to the base class at all... so the ServiceBase class never has a chance to respond gracefully to the SCM... you are just unilaterally terminating the process hosting your service. This is not what a well-behaved Windows service does.
The ServiceBase class is designed to be able to support multiple services hosted in a single service process. Individual services should not concern themselves with the lifetime of the host service process, only with the logical state of their own service.

Start Windows Service in C#

I want to start a windows service that was just installed.
ServiceBase[] ServicesToRun;
if (bool.Parse(System.Configuration.ConfigurationManager.AppSettings["RunService"]))
{
ServicesToRun = new ServiceBase[] { new IvrService() };
ServiceBase.Run(ServicesToRun);
}
The IvrService code is:
partial class IvrService : ServiceBase
{
public IvrService()
{
InitializeComponent();
Process myProcess;
myProcess = System.Diagnostics.Process.GetCurrentProcess();
string pathname = Path.GetDirectoryName(myProcess.MainModule.FileName);
//eventLog1.WriteEntry(pathname);
Directory.SetCurrentDirectory(pathname);
}
protected override void OnStart(string[] args)
{
string sProcessName = Process.GetCurrentProcess().ProcessName;
if (Environment.UserInteractive)
{
if (sProcessName.ToLower() != "services.exe")
{
// In an interactive session.
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new IvrInteractive());
IvrApplication.Start(); // the key function of the service, start it here
return;
}
}
}
I am not sure how to start the service. Using ServiceController.Start()? But I already have ServiceBase.Run(ServicesToRun); Is it for starting a service?
Code hint is definitely appreciated.
To answer the question about starting a service from code, you would want something like this (which would be equivalent to running net start myservice from the command line):
ServiceController sc = new ServiceController();
sc.ServiceName = "myservice";
if (sc.Status == ServiceControllerStatus.Running ||
sc.Status == ServiceControllerStatus.StartPending)
{
Console.WriteLine("Service is already running");
}
else
{
try
{
Console.Write("Start pending... ");
sc.Start();
sc.WaitForStatus(ServiceControllerStatus.Running, new TimeSpan(0, 0, 10));
if (sc.Status == ServiceControllerStatus.Running)
{
Console.WriteLine("Service started successfully.");
}
else
{
Console.WriteLine("Service not started.");
Console.WriteLine(" Current State: {0}", sc.Status.ToString("f"));
}
}
catch (InvalidOperationException)
{
Console.WriteLine("Could not start the service.");
}
}
This will start the service, but keep in mind that it will be a different process than the one that is executing the above code.
Now to answer the question about debugging a service.
One option is to attach after the service has been started.
The other is to make your service executable be able to run the main code, not as a service but as a normal executable (typically setup via a command line parameter). Then you can F5 into it from your IDE.
EDIT: Adding sample flow of events (based on questions from some of the comments)
OS is asked to start a service. This can be done from the control panel, the command line, APIs (such as the code above), or automatically by the OS (depending on the service's startup settings).
The operating system then creates a new process.
The process then registers the service callbacks (for example ServiceBase.Run in C# or StartServiceCtrlDispatcher in native code). And then starts running its code (which will call your ServiceBase.OnStart() method).
The OS can then request that a service be paused, stopped, etc. At which point it will send a control event to the already running process (from step 2). Which will result in a call to your ServiceBase.OnStop() method.
EDIT: Allowing a service to run as a normal executable or as a command line app: One approach is to configure your app as a console application, then run different code based on a command line switch:
static void Main(string[] args)
{
if (args.Length == 0)
{
// we are running as a service
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run(ServicesToRun);
}
else if (args[0].Equals("/debug", StringComparison.OrdinalIgnoreCase))
{
// run the code inline without it being a service
MyService debug = new MyService();
// use the debug object here
}
First you need to install the service using InstallUtil, e.g.
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\InstallUtil.exe C:\MyService.exe
You need to start the service from the command line, e.g.
net start ServiceName // whatever the service is called
Then you need to attach to the process with Visual Studio, via Tools > Attach To Process.
Stick the breakpoints in the solution wherever you want it to break and the dev environment should take over.
To start it from the dev environment, put the net start ServiceName command into a batch file, and then in the Project Properties > Build Events "Post Build Event Command Line" you can add the path to the batch file.
*note that looking now, I'm not sure if you need to even use a batch file, you might be able to put the command directly in there. Try and edit this answer with whatever works.

Categories