场景:公司写的程序老是崩掉,各种改版无果,遂写一个守护进程来监听该程序,如果崩掉,就自动重启。

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程序已停止工作”的提示框,崩溃程序进程会自动退出。
这种修改系统注册表的方法是最方便和直接的,但会对所有程序生效。

如图:

ingress 监听443端口必须配置证书吗_xml

 

扩展:如果用配置文件保存信息,按规范只能存一条,如果我们要守护多个程序,就需要保存多条信息,于是将信息保存到xml文件中来处理。

1. 添加  DataSet.xml 文件

ingress 监听443端口必须配置证书吗_FileStream_02

ingress 监听443端口必须配置证书吗_System_03

<?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. 代码迭代

ingress 监听443端口必须配置证书吗_FileStream_02

ingress 监听443端口必须配置证书吗_System_03

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