C#进程监控小程序

2021-05-29  本文已影响0人  蓬篙人

背景

由于历史原因,存在一些旧服务程序,无法修改其源代码,但是某个客户更换新服务器后,可能由于兼容性问题导致服务经常会掉线或报错卡死。因此,开发一个程序对指定的服务进程进行监控,在服务掉线后自动重启服务,在服务报错卡死时自动关掉服务进程并重启。

程序设计

程序是使用C#开发的一个托盘程序,主要包含操作系统进程监测,操作系统应用程序日志监测和服务程序维护三部分功能。


监控程序框架图

程序实现

1. C#托盘程序

C#的托盘程序是使用NotifyIcon控件来实现。主要步骤如下:

private void FormMain_Load(object sender, EventArgs e)
{
    Close();
}
private void FormMain_FormClosing(object sender, EventArgs e)
{
    if (e.CloseReason != CloseReason.WindowShutDown)
    {
        e.Cancel = true;
        Hide();
        ShowInTaskbar = false;
        nIconMonitor.Visible = true;      // show NotifyIcon
    }
}
// nIconMonitor为NotifyIcon控件,cmsMonitor为ContextMenuStrip控件
private void nIconMonitor_MouseDown(object sender, MouseEventArgs e)
{
    if(e.Button == MouseButtons.Right)
    {
        nIconMonitor.ContextMenuStrip = cmsMonitor;
    }
}

2. 进程监测实现

主要使用System.Diagnostics.Process类获取当前正在运行的所有进程,判断被监控服务程序是否存在或被标记为错误。主要代码如下:

Process[] sysProcess = Process.GetProcesses();
List<int> killIndex = new List<int>();
for (int i = 0; i < sysProcess.Length; i++)
{
    string processName = sysProcess[i].ProcessName;
    if (mMonitorServices.Services.ContainsKey(processName))
    {
         string path = mMonitorServices.Services[processName].Path;
         if (path == sysProcess[i].MainModule.FileName)
         {
            if (mErrorApp.Contains(processName))
            {
               killIndex.Add(i);
            }
           else
                mMonitorServices.Services[processName].Reboot = false;
         }
    }
}
for (int i = 0; i < killIndex.Count; i++)
{
    string processName = sysProcess[killIndex[i]].ProcessName;
    sysProcess[killIndex[i]].Kill();
    mMonitorServices.Services[processName].Reboot = true;
    mErrorApp.Remove(processName);
}

程序重启直接调用Process.Start("path")即可,其中path为程序绝对路径。

3. 日志监测实现

操作系统应用程序错误日志监测主要使用EventLogWatcherEventLogQuery这两个类,其中EventLogQuery设定日志的查询条件,而通过EventLogWatcherEventRecordWritten事件捕获错误日志并进行服务程序错误标记。

private void StartEventLogWatcher()
{
    EventLogQuery query = new EventLogQuery("Application", PathType.FilePath, "*[System/Level=2]");
    mWatcher = new EventLogWatcher(query);
    mWatcher.EventRecordWritten += Watcher_EventRecordWritten;
    mWatcher.Enabled = true;
}

private void Watcher_EventRecordWritten(object sender, EventRecordWrittenEventArgs e)
{
    if (e.EventRecord != null)
    {
        var properties = e.EventRecord.Properties;
        foreach (var data in properties)
        {
            string info = data.Value.ToString();
            if(info.Contains(".exe"))
            {
                string appName = info.Replace(".exe", "");
                if (mMonitorServices.Services.ContainsKey(appName))
                {
                    mErrorApp.Add(appName);
                    break;
                }
            }
        }
    }
}

4. 服务程序维护

因为只是一个小程序,所以监控的服务程序信息使用xml文件保存和维护。因此该模块功能主要是xml文件的一些操作。保存服务程序的xml文件格式如下:

<?xml version="1.0" encoding="utf-8" ?>
<services>
  <service name="Service1">
    <path>D:\Services\DicomService.exe</path>
  </service>
</services>

其中每一个监控的服务程序使用一个<service>节点进行保存,节点的name属性保存服务程序的名称(不含程序后缀.exe),子节点<path>保存服务程序的绝对路径。维护类代码如下:

using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using System.Xml;

namespace WinformServiceMonitor
{
    public class ServicesXml
    {
        private readonly string mFileName = $"{Application.StartupPath}\\Services.xml";
        // 保存监控的服务程序字典
        private Dictionary<string, MonitorService> mServicesDict = new Dictionary<string, MonitorService>();

        public Dictionary<string, MonitorService> Services
        {
            get { return mServicesDict; }
        }

        public ServicesXml()
        {
            if (!File.Exists(mFileName))
                CreateXmlFile();
            ReadServices();
        }

        private void ReadServices()
        {
            XmlDocument document = new XmlDocument();
            document.Load(mFileName);
            XmlNode root = document.SelectSingleNode("services");
            foreach(XmlNode node in root.ChildNodes)
            {
                string name = node.Attributes["name"].Value;
                if (!mServicesDict.ContainsKey(name))
                {
                    mServicesDict.Add(name, new MonitorService { Name = name, Path = node["path"].InnerText });
                }
            }
        }

        public void AddServiceNode(string name, string path)
        {
            XmlDocument document = new XmlDocument();
            document.Load(mFileName);
            XmlNode root = document.SelectSingleNode("services");

            XmlElement serviceNode = document.CreateElement("service");
            serviceNode.SetAttribute("name", name);
            XmlElement pathNode = document.CreateElement("path");
            pathNode.InnerText = path;
            serviceNode.AppendChild(pathNode);
            root.AppendChild(serviceNode);

            mServicesDict.Add(name, new MonitorService { Name = name, Path = path, Reboot = false });

            document.Save(mFileName);
        }

        public void RemoveServiceNode(string name)
        {
            XmlDocument document = new XmlDocument();
            document.Load(mFileName);
            XmlNode root = document.SelectSingleNode("services");
            bool remove = false;
            foreach (XmlNode node in root.ChildNodes)
            {
                if (node.Attributes["name"].Value == name)
                {
                    root.RemoveChild(node);
                    remove = true;
                    break;
                }
            }
            if (remove)
                mServicesDict.Remove(name);
            document.Save(mFileName);
        }

        private void CreateXmlFile()
        {
            XmlDocument document = new XmlDocument();
            XmlDeclaration declaration = document.CreateXmlDeclaration("1.0", "UTF-8", "");
            document.AppendChild(declaration);

            XmlElement services = document.CreateElement("services");
            document.AppendChild(services);
            document.Save(mFileName);
        }
    }
}
namespace WinformServiceMonitor
{
    // 监控服务程序实体类
    public class MonitorService
    {
        /// <summary>
        /// 服务程序名称
        /// </summary>
        public string Name { get; set; }
        /// <summary>
        /// 服务绝对路径
        /// </summary>
        public string Path { get; set; }
        /// <summary>
        /// 是否需要重启
        /// </summary>
        public bool Reboot { get; set; }

        public MonitorService()
        {
            Reboot = false;
        }
    }
}

参考文章

上一篇 下一篇

猜你喜欢

热点阅读