This article is contributed by a reader.
Author: Pride and Prejudice
Original title: Using C# to simply create a watchdog program
Original link: https://www.cnblogs.com/chonglu/p/16913746.html
First, thank you for the reader's support:

We welcome readers to submit technical articles, no topic restrictions, but there is no payment...

Summary
In some special projects, the software may be unattended. If the program unexpectedly crashes or the process is killed, etc., developing a watchdog program is very necessary. It is like an indestructible pest; as long as the program exits abnormally, it can immediately restart the guarded program.
Code Implementation
Tips: The complete source code is at the end of the article, so I won't write it step by step here.
- Create a
Dogclass, mainly used to periodically scan whether the guarded program is still running.
A timer is opened to check every 5 seconds. If no process is found, use Process to start the program.
public class Dog
{
private Timer timer = new Timer();
private string processName ;
private string filePath;// Path of the program to monitor
public Dog()
{
timer.Interval = 5000;
timer.Tick += timer_Tick;
}
public void Start(string filePath)
{
this.filePath = filePath;
this.processName = Path.GetFileNameWithoutExtension(filePath);
timer.Enabled = true;
}
/// <summary>
/// Periodically check if the system is running
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void timer_Tick(object sender, EventArgs e)
{
try
{
Process[] myproc = Process.GetProcessesByName(processName);
if (myproc.Length == 0)
{
Log.Info("Detected that the guarded program has exited, starting to reactivate the program, program path:{0}",filePath);
ProcessStartInfo info = new ProcessStartInfo
{
WorkingDirectory = Path.GetDirectoryName(filePath),
FileName = filePath,
UseShellExecute = true
};
Process.Start(info);
Log.Info("Guarded program started");
}
}
catch (Exception)
{
}
}
}
- At the program entry, receive the path of the guarded program and start the
Dogscanning.
static class Program
{
static NotifyIcon icon = new NotifyIcon();
private static Dog dog = new Dog();
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main(string[] args)
{
if (args == null || args.Length == 0)
{
MessageBox.Show("Startup parameter error", "Prompt", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
string filePath = args[0];
if(!File.Exists(filePath))
{
MessageBox.Show("Startup parameter error", "Prompt", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
Process current = Process.GetCurrentProcess();
Process[] processes = Process.GetProcessesByName(current.ProcessName);
// Iterate through the list of processes with the same name as the current process
foreach (Process process in processes)
{
// If an instance already exists, ignore the current process
if (process.Id != current.Id)
{
// Ensure the process to be opened comes from the same file path as the existing process
if (process.MainModule.FileName.Equals(current.MainModule.FileName))
{
// Existing process
return;
}
else
{
process.Kill();
process.WaitForExit(3000);
}
}
}
icon.Text = "Watchdog";
icon.Visible = true;
Log.Info("Starting watchdog, guarded program:{0}",filePath);
dog.Start(filePath);
Application.Run();
}
}
- Simply implement a logger (a third-party library can also be used, but it is recommended that the watchdog program have no dependencies). You can also directly use the following one, which is very simple and has no dependencies.
public class Log
{
// Reader-writer lock: when a resource is in write mode, other threads must wait until the current write finishes
private static ReaderWriterLockSlim LogWriteLock = new ReaderWriterLockSlim();
// Log file path
public static string logPath = "logs\\dog.txt";
// Static constructor: automatically called before processing any type, to check if the log file exists
static Log()
{
// Create folder
if (!Directory.Exists("logs"))
{
Directory.CreateDirectory("logs");
}
}
/// <summary>
/// Write log.
/// </summary>
public static void Info(string format, params object[] args)
{
try
{
LogWriteLock.EnterWriteLock();
string msg = args.Length > 0 ? string.Format(format, args) : format;
using (FileStream stream = new FileStream(logPath, FileMode.Append))
{
StreamWriter write = new StreamWriter(stream);
string content = String.Format("{0} {1}",DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),msg);
write.WriteLine(content);
// Close and dispose the stream to write to file
write.Close();
write.Dispose();
}
}
catch (Exception e)
{
}
finally
{
LogWriteLock.ExitWriteLock();
}
}
}
At this point, the watchdog program is complete. Next, encapsulate a start/stop class in the main program (the guarded program).
- Main program encapsulates watchdog start/stop class.
public static class WatchDog
{
private static string processName = "WatchDog"; // Watchdog process name (note: this is not the guarded program name; try replacing it with the main program name to see the effect)
private static string appPath = AppDomain.CurrentDomain.BaseDirectory; // System startup directory
/// <summary>
/// Start watchdog
/// </summary>
public static void Start()
{
try
{
string program = string.Format("{0}{1}.exe", appPath, processName);
ProcessStartInfo info = new ProcessStartInfo
{
WorkingDirectory = appPath,
FileName = program,
CreateNoWindow = true,
UseShellExecute = true,
Arguments = Process.GetCurrentProcess().MainModule.FileName // Full path of the guarded program
};
Process.Start(info);
}
catch (Exception)
{
}
}
/// <summary>
/// Stop watchdog
/// </summary>
public static void Stop()
{
Process[] myproc = Process.GetProcessesByName(processName);
foreach (Process pro in myproc)
{
pro.Kill();
pro.WaitForExit(3000);
}
}
}
The principle is also simple, with two points to note:
- The
processNamefield represents the watchdog program, not the guarded program. If you get it wrong, well... (you can try the effect) - The
Argumentsparameter is the full path of the guarded program, because generally the guarded program starts the watchdog program. So we can directly useProcess.GetCurrentProcess().MainModule.FileNameto obtain the full path of the guarded program.
- Start the watchdog at the main program entry point.
public partial class App : Application
{
[STAThread]
static void Main()
{
// Call the watchdog program before the program starts
WatchDog.Start();
Application app = new Application();
MainWindow mainWindow = new MainWindow();
app.Run(mainWindow);
}
}
Entry points for Winform, regular WPF, Prism, etc. are different; handle flexibly according to the actual project situation.
Finally, stop the watchdog program where the main program needs to exit normally (i.e., the main program's close button or other places where a normal exit is desired).
Effect
