美文网首页
C#进程监控小程序

C#进程监控小程序

作者: 蓬篙人 | 来源:发表于2021-05-29 16:56 被阅读0次

    背景

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

    程序设计

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


    监控程序框架图
    • 操作系统进程监测:定时获取当前操作系统所有正在运行的进程进行循环判断。如果被监控服务程序存在且非标记为错误进程则无需重启;如果被监控服务程序不存在或被标记为错误进程则进行重启操作。流程如下:


      进程监控流程
    • 操作系统应用程序日志监测:捕获操作系统应用程序中的错误日志,并根据日志信息,判断是否为所监控的服务程序,是则将服务程序名称放入错误服务程序列表中。

    • 服务程序维护:使用xml文件保存需要监控的服务程序名称和路径。

    程序实现

    1. C#托盘程序

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

    • 窗体Load事件中关闭窗体
    private void FormMain_Load(object sender, EventArgs e)
    {
        Close();
    }
    
    • 窗体FormClosing事件中取消窗体关闭和显示NotifyIcon控件等。注意:一定要给NotifyIcon控件设置图标,不然电脑右下角不会显示。
    private void FormMain_FormClosing(object sender, EventArgs e)
    {
        if (e.CloseReason != CloseReason.WindowShutDown)
        {
            e.Cancel = true;
            Hide();
            ShowInTaskbar = false;
            nIconMonitor.Visible = true;      // show NotifyIcon
        }
    }
    
    • 根据需要添加ContextMenuStrip控件及相应菜单项,在NotifyIconMouseDown事件中绑定。下面代码采用右键显示菜单:
    // 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;
                    }
                }
            }
        }
    }
    
    • EventLogQuery类中的"Application"指定监测应用程序日志,"*[System/Level=2]"指定捕获级别为2的日志,即错误日志。

    • e.EventRecord.Properties是日志记录保存自定义数据的属性,下图中的EventData。该属性是一个IList<EventProperty>对象,我们主要是想获取其中表示程序名称的信息。

      应用程序错误日志.png
    • mErrorApp是一个保存错误服务程序名称的List<string>对象。

    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;
            }
        }
    }
    
    • 注意:其中为了方便,添加和移除功能都是直接打开xml文件进行操作,如果不是这种小程序不建议这样操作。

    参考文章

    相关文章

      网友评论

          本文标题:C#进程监控小程序

          本文链接:https://www.haomeiwen.com/subject/pgbysltx.html