【作者】科技、互联网行业优质创作者
【专注领域】.Net技术、软件架构、人工智能、数字化转型、DeveloperSharp、微服务、工业互联网、智能制造
点击右上方“关注”,里面有很多高价值技术文章,是你刻苦努力也积累不到的经验,能助你快速成长。升职 涨薪!!
前言
在有些特殊项目中,软件可能是无人值守的,如果程序莫名其妙挂了或者进程被干掉了等等,这时开发一个看门狗程序是非常有必要的,它就像一只打不死的小强,只要程序非正常退出,它就能立即再次将被看护的程序启动起来。
代码实现
Tips:文末有完整源代码,就不一步一步写了
1、创建一个Dog类,主要用于间隔性扫描被看护程序是否还在运行
开了个定时器,每5秒去检查1次,如果没有找到进程则使用Process启动程序
publicclassDog
{
privateTimertimer=newTimer();
privatestringprocessName;
privatestringFilePath;//要监控的程序的路径
publicDog()
{
timer.Interval=5000;
timer.Tick =timer_Tick;
}
publicvoidStart(stringfilePath)
{
this.filePath=filePath;
this.processName=Path.GetFileNameWithoutExtension(filePath);
timer.Enabled=true;
}
///<summary>
///定时检测系统是否在运行
///</summary>
///<paramname="sender"></param>
///<paramname="e"></param>
privatevoidtimer_Tick(objectsender,EventArgse)
{
try
{
Process[]myproc=Process.GetProcessesByName(processName);
if(myproc.Length==0)
{
Log.Info("检测到看护程序已退出,开始重新激活程序,程序路径:{0}",filePath);
ProcessStartInfoinfo=newProcessStartInfo
{
WorkingDireCTOry=Path.GetDirectoryName(filePath),
FileName=filePath,
UseShellExecute=true
};
Process.Start(info);
Log.Info("看护程序已启动");
}
}
catch(Exception)
{
}
}
}
2、在程序入口接收被看护程序的路径,启动Dog扫描
staticclassProgram
{
staticNotifyiconicon=newNotifyIcon();
privatestaticDogdog=newDog();
///<summary>
///应用程序的主入口点。
///</summary>
[STAThread]
staticvoidMain(string[]args)
{
if(args==null||args.Length==0)
{
MessageBox.Show("启动参数异常","提示",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
stringfilePath=args[0];
if(!File.Exists(filePath))
{
MessageBox.Show("启动参数异常","提示",MessageBoxButtons.OK,MessageBoxIcon.Error);
return;
}
Processcurrent=Process.GetCurrentProcess();
Process[]processes=Process.GetProcessesByName(current.ProcessName);
//遍历与当前进程名称相同的进程列表
foreach(Processprocessinprocesses)
{
//如果实例已经存在则忽略当前进程
if(process.Id!=current.Id)
{
//保证要打开的进程同已经存在的进程来自同一文件路径
if(process.MainModule.FileName.Equals(current.MainModule.FileName))
{
//已经存在的进程
return;
}
else
{
process.Kill();
process.WaitForExit(3000);
}
}
}
icon.Text="看门狗";
icon.Visible=true;
Log.Info("启动看门狗,看护程序:{0}",filePath);
dog.Start(filePath);
Application.Run();
}
}
3、简单实现个日志记录器(使用第三方库也行,建议看护程序最好不要有任何依赖),也可直接使用我下面这个,很简单,无任何依赖
publicclassLog
{
//读写锁,当资源处于写入模式时,其他线程写入需要等待本次写入结束之后才能继续写入
privatestaticReaderWriterLockSlimLogWriteLock=newReaderWriterLockSlim();
//日志文件路径
publicstaticstringlogPath="logs\\dog.txt";
//静态方法todo:在处理话类型之前自动调用,去检查日志文件是否存在
staticLog()
{
//创建文件夹
if(!Directory.Exists("logs"))
{
Directory.CreateDirectory("logs");
}
}
///<summary>
///写入日志.
///</summary>
publicstaticvoidInfo(stringformat,paramsobject[]args)
{
try
{
LogWriteLock.EnterWriteLock();
stringmsg=args.Length>0?string.Format(format,args):format;
using(FileStreamstream=newFileStream(logPath,FileMode.Append))
{
StreamWriterwrite=newStreamWriter(stream);
stringcontent=String.Format("{0}{1}",DateTime.Now.ToString("yyyy-MM-ddHH:mm:ss"),msg);
write.WriteLine(content);
//关闭并销毁流写入文件
write.Close();
write.Dispose();
}
}
catch(Exceptione)
{
}
finally
{
LogWriteLock.ExitWriteLock();
}
}
}
至此,看护程序已经搞定。接着在主程序(被看护程序)封装一个启停类
4、主程序封装看门狗启停类
publicstaticclassWatchDog
{
privatestaticstringprocessName="WatchDog";//看护程序进程名(注意这里不是被看护程序名,你可以试一下换成主程序名字会使什么效果)
privatestaticstringappPath=AppDomain.CurrentDomain.BaseDirectory;//系统启动目录
///<summary>
///启动看门狗
///</summary>
publicstaticvoidStart()
{
try
{
stringprogram=string.Format("{0}{1}.exe",appPath,processName);
ProcessStartInfoinfo=newProcessStartInfo
{
WorkingDirectory=appPath,
FileName=program,
CreateNoWindow=true,
UseShellExecute=true,
Arguments=Process.GetCurrentProcess().MainModule.FileName//被看护程序的完整路径
};
Process.Start(info);
}
catch(Exception)
{
}
}
///<summary>
///停用看门狗
///</summary>
publicstaticvoidStop()
{
Process[]myproc=Process.GetProcessesByName(processName);
foreach(Processproinmyproc)
{
pro.Kill();
pro.WaitForExit(3000);
}
}
}
原理也很简单,其中有两点需要注意:
- processName字段表示看护程序,不是被看护程序,如果写反了,嗯...(你可以试下效果)‘
- Arguments参数是被看护程序的完整路径,因为一般情况下,是由被看护程序启动看护程序,所以我们可以直接使用Process.GetCurrentProcess().MainModule.FileName获取到被看护程序的完整路径
5、在主程序入口点启动看门狗
publicpartialclassApp:Application
{
[STAThread]
staticvoidMain()
{
//程序启动前调用看护程序
WatchDog.Start();
Applicationapp=newApplication();
MainWindowmainWindow=newMainWindow();
app.Run(mainWindow);
}
}
Winform、普通WPF、Prism等入口点都不太一样,根据项目实际情况灵活处理即可
最后在需要正常退出程序的地方(也就是主程序关闭按钮或其它想要正常退出程序的地方)停止看门狗程序
效果
源代码
https://github.com/luchong0813/WatchDogDemo
后续
如果是别人的 建议使用nssm把别人的程序 封装成服务由wondows去管理,如果是自己的 需要弹框跟用户反馈或交互,那就说明不是无人值守,可以不用看门狗。
在类似地铁进站通道、机场安检通道、口岸出入境通道等等都有一个引导程序,这种比较依赖图形界面的可以尝试使用看门狗程序,看门狗程序不依赖第三方库且代码量较少,一般不会跑飞。
写在最后- 请点击上方“关注”我,里面有很多高价值技术文章,是你刻苦努力也积累不到的经验,能助你快速成长。升职 涨薪!!
- 再分享一个全网最全.NET/C#视频学习教程,能让你走遍初级、中级、高级、架构师、CTO的所有路径:
领取方式:在我的个人主页的第一篇置顶文章中领取