场景:公司写的程序老是崩掉,各种改版无果,遂写一个守护进程来监听该程序,如果崩掉,就自动重启。
1. 添加进程管理类 ProcessHelper
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Guard
{
public class ProcessHelper
{
/// <summary>
/// 查看进程是否存在
/// </summary>
/// <param name="name">进程名称</param>
/// <returns></returns>
public static bool Exist(string name)
{
return Process.GetProcessesByName(name).Length > 0;
}
/// <summary>
/// 开启进程
/// </summary>
/// <param name="path">进程所在物理路径</param>
/// <returns></returns>
public static void Start(string path)
{
Process m_Process = new Process();
m_Process.StartInfo.FileName = path;
//开启新的窗体
m_Process.StartInfo.UseShellExecute = true;
m_Process.Start();
}
/// <summary>
/// 关闭进程
/// </summary>
/// <param name="name">进程名称</param>
/// <returns></returns>
public static void Stop(string name)
{
Process proc = Process.GetProcessesByName(name).FirstOrDefault();
if (proc != null)
proc.Kill();
}
}
}
2. 添加配置文件 App.config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!--守护间隔时间-->
<add key="second" value="10"/>
<!--守护程序端口-->
<add key="port" value="104"/>
<!--守护程序路径-->
<add key="path" value="D:\项目文件\某某系统\XXXXX\bin\Debug\XXXXXXXXXX.exe"/>
</appSettings>
</configuration>
3. 在类型库里面添加引用 System.configuration,方便获取配置文件自定义的值
4. 实现代码
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
namespace Guard
{
class Program
{
static void Main(string[] args)
{
try
{
int second = Convert.ToInt32(ConfigurationManager.AppSettings["second"]);
int port = Convert.ToInt32(ConfigurationManager.AppSettings["port"]);
string path = ConfigurationManager.AppSettings["path"];
while (true)
{
Console.WriteLine("Port: " + port + " status: " + (PortInUse(port) ? "存在" : "不存在"));
if (!PortInUse(port)) //如果监听的端口不存在,就启动对应的程序
{
WriteLog("启动端口" + port + "的程序");
ProcessHelper.Start(path);
}
Thread.Sleep(second * 1000);
}
}
catch (Exception ex)
{
WriteLog("发生错误:" + ex.Message);
}
}
/// <summary>
/// 日志记录
/// </summary>
/// <param name="errMsg"></param>
private static void WriteLog(string errMsg)
{
string sFilePath = AppDomain.CurrentDomain.BaseDirectory + "\\Log";
string sFileName = DateTime.Now.ToString("yyyy-MM-dd") + ".log";
sFileName = sFilePath + "\\" + sFileName;
Directory.CreateDirectory(sFilePath);
FileStream fs;
StreamWriter sw;
if (File.Exists(sFileName))
fs = new FileStream(sFileName, FileMode.Append, FileAccess.Write);
else
fs = new FileStream(sFileName, FileMode.Create, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine("时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
sw.WriteLine("信息:" + errMsg);
sw.WriteLine("");
sw.Close();
fs.Close();
}
/// <summary>
/// 判断端口是否占用
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public static bool PortInUse(int port)
{
bool inUse = false;
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in ipEndPoints)
{
if (endPoint.Port == port)
{
inUse = true;
break;
}
}
return inUse;
}
}
}
其他:程序崩掉,但没有自动关闭控制台的窗体,便不能通过端口监听到是否崩掉,遂修改系统注册表:
运行注册表编辑器,依次定位到 HKEY_CURRENT_USER\Software\Microsoft\Windows\WindowsError Reporting,
在右侧窗口中找到并双击打开DontshowUI,然后在弹出的窗口中将默认值“0”修改为“1”。
那么,当程序崩溃时,就不会再出现”xx程序已停止工作”的提示框,崩溃程序进程会自动退出。
这种修改系统注册表的方法是最方便和直接的,但会对所有程序生效。
如图:
扩展:如果用配置文件保存信息,按规范只能存一条,如果我们要守护多个程序,就需要保存多条信息,于是将信息保存到xml文件中来处理。
1. 添加 DataSet.xml 文件
<?xml version="1.0" encoding="utf-8" ?>
<Monitor>
<Second>10</Second>
<Programs>
<Program>
<Port>104</Port>
<Path>D:\项目文件\某某系统\XXXXX\bin\Debug\XXXXXXX.exe</Path>
</Program>
<Program>
<Port>105</Port>
<Path>D:\项目文件\某某系统\XXXXX\bin\Debug - 副本\XXXXXXX.exe</Path>
</Program>
</Programs>
</Monitor>
View Code
2. 代码迭代
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Threading;
using System.Xml;
namespace Guard
{
class Program
{
static void Main(string[] args)
{
try
{
int second = 120; //守护间隔时间
DataTable dt = Select("DataSet.xml", out second);
while (true)
{
foreach (DataRow row in dt.Rows)
{
if (!PortInUse(Convert.ToInt32(row["port"]))) //如果监听的端口不存在,就启动对应的程序
{
Console.WriteLine("启动端口 " + row["port"] + " 的程序");
WriteLog("启动端口 " + row["port"] + " 的程序");
ProcessHelper.Start(row["path"].ToString());
}
}
Thread.Sleep(second * 1000);
}
}
catch (Exception ex)
{
WriteLog("发生错误:" + ex);
Console.WriteLine(ex);
}
}
/// <summary>
/// 查询xml值
/// </summary>
/// <param name="xmlPath"></param>
/// <param name="second"></param>
/// <returns></returns>
public static DataTable Select(string xmlPath, out int second)
{
XmlDocument xmlDoc = new XmlDocument();
try
{
xmlDoc.Load(xmlPath);
//var root = xmlDoc.DocumentElement;//取到根结点
XmlNode secondChild = xmlDoc.SelectSingleNode("Monitor/Second"); //取指定的单个结点
second = Convert.ToInt32(secondChild.InnerText);
XmlNode xmldt = xmlDoc.SelectSingleNode("Monitor/Programs");
DataSet ds = GetDataSet(xmldt.OuterXml);
return ds.Tables["Program"];
}
catch (Exception ex)
{
WriteLog("发生错误1:" + ex);
Console.WriteLine(ex);
second = 120;
return null;
}
}
/// <summary>
/// 读取Xml字符串返回DataSet
/// </summary>
/// <param name="StringXml">字符串</param>
/// <returns>DataSet</returns>
public static DataSet GetDataSet(string StringXml)
{
StringReader stringReader = null;
XmlTextReader xmlTextReader = null;
DataSet rDataSet = new DataSet();
try
{
stringReader = new StringReader(StringXml);
xmlTextReader = new XmlTextReader(stringReader);
rDataSet.ReadXml(xmlTextReader);
}
catch (Exception ex)
{
WriteLog("发生错误2:" + ex);
Console.WriteLine(ex);
}
finally
{
if (xmlTextReader != null)
{
xmlTextReader.Close();
stringReader.Close();
stringReader.Dispose();
}
}
return rDataSet;
}
/// <summary>
/// 日志记录
/// </summary>
/// <param name="errMsg"></param>
private static void WriteLog(string errMsg)
{
string sFilePath = AppDomain.CurrentDomain.BaseDirectory + "\\Log";
string sFileName = DateTime.Now.ToString("yyyy-MM-dd") + ".log";
sFileName = sFilePath + "\\" + sFileName;
Directory.CreateDirectory(sFilePath);
FileStream fs;
StreamWriter sw;
if (File.Exists(sFileName))
fs = new FileStream(sFileName, FileMode.Append, FileAccess.Write);
else
fs = new FileStream(sFileName, FileMode.Create, FileAccess.Write);
sw = new StreamWriter(fs);
sw.WriteLine("时间:" + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
sw.WriteLine("信息:" + errMsg);
sw.WriteLine("");
sw.Close();
fs.Close();
}
/// <summary>
/// 判断端口是否占用
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public static bool PortInUse(int port)
{
bool inUse = false;
IPGlobalProperties ipProperties = IPGlobalProperties.GetIPGlobalProperties();
IPEndPoint[] ipEndPoints = ipProperties.GetActiveTcpListeners();
foreach (IPEndPoint endPoint in ipEndPoints)
{
if (endPoint.Port == port)
{
inUse = true;
break;
}
}
return inUse;
}
}
}
View Code