利用WCF的双工通讯实现一个简单的心跳监控系统
何为心跳监控系统?
故名思义,就是监控某个或某些个程序的运行状态,就好比医院里面的心跳监视仪一样,能够随时显示病人的心跳情况。
心跳监控的目的是什么?
与医院里面的心跳监视仪目的类似,监控程序运行状态,一旦出现问题(比如:一些自动运行的服务、程序等突然停止运行了),那么心跳监控系统就能“感知到”并及时的显示在监控界面上,同时可以通过微信、短信告之相关的人员,以便他们及时处理程序异常,从而避免一些自动运行的服务、程序等突然停止运行而造成的一系列损失
心跳监控系统实现的思路是怎样的?
核心技术:WCF的双工通讯
实现步骤:
1.定义WCF的服务契约接口以及回调接口,服务方法主要包括:Start(被监控的程序启动时调用,即通知监控系统,我已经启动了)、Stop(被监控的程序停止时调用,即通知监控系统,我已经停止了)、ReportRunning(被监控的程序运行中定时调用,即通知监控系统,我是正常的并在运行中,同时还起到检测监控系统是否在运行的一个功能,一举两得),回调服务方法应有:Listen(这个是给监控系统主动去定时回调被监控的程序(心跳),如果被监控的程序能正常的返回状态,那么就是正常的,否则有可能已经“死了”,这时监控系统就需要按照预设指令作出相应的操作,比如:监控主界面显示异常的程序,同时发送异常通知消息给相关人员)
2.建立一个心跳监控系统Winform项目,并实现WCF服务,即集成实现WCF服务宿主程序,同时每一个WCF服务方法均需关联界面,即:程序启动了、停止了、运行中均会在界面实时显示出来或做一些实时统计;
3.其它被监控的程序(指自动运行的服务、程序等)需要集成实现WCF回调接口及开启WCF服务通讯的功能;
心跳监控系统的运行顺序是怎样的?如何保证监控方与被监控方实时不间断通讯?
运行顺序:
1.开启心跳监控系统(即:同时开启WCF服务宿主程序),确保监控服务正常运行;
2.开启其它被监控的程序,确保开启客户端与监控系统的WCF双工通讯服务正常;
注意:一定要先开启心跳监控系统,否则其它被监控的程序因无法与监控系统的WCF双工通讯服务正常连接而报错或多次尝试重连;
保证监控方与被监控方实时不间断通讯:
在保证按照上面所说的运行顺序依次开启心跳监控系统,再开启其它被监控的程序,正常建立一次通讯后,后续只要任何一方出现通讯中断,均会自动尝试重连(主要是客户端重连,心跳监控系统除非停止运行,否则不会中断,若因停止运行造成双方通讯中断,只需重启心跳监控系统即可)
通讯模式:
推模式:被监控的程序通过主动的调用WCF服务方法:Start、Stop、ReportRunning 向心跳监控系统告之运行状态,若通讯失败,则自动尝试重连,直至连接成功;
拉模式:心跳监控系统主动回调Listen方法,向被监控的程序索取运行状态,若通讯失败,则会在指定时间内多次重试回调客户端,若客户端在规定的时间范围内仍无法返回消息,则视为客户端异常,那么心跳监控系统则会进行异常的处理;
实现源代码:
ProgramMonitor.Core 类库项目:主要是定义WCF服务的相关接口以及实现回调的接口(因为每个客户端都需实现一遍回调接口,故统一实现,客户端集成后直接可以用,简化集成客户端的开发成本),心跳监控系统及需要被监控的程序均需要依赖该DLL;
ProgramMonitor WINFORM项目:心跳监控系统
整个解决方案如下图示:
注:由于源代码相对较多,故不再一一说明,只对重点的代码作解释
ProgramMonitor.Core
IListenService:(心跳监控WCF服务契约接口)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace ProgramMonitor.Core { [ServiceContract(Namespace = "http://www.zuowenjun.cn", SessionMode = SessionMode.Required, CallbackContract = typeof(IListenCall))] public interface IListenService { [OperationContract(IsOneWay = true)] void Start(ProgramInfo programInfo); [OperationContract(IsOneWay = true)] void Stop(string programId); [OperationContract(IsOneWay = true)] void ReportRunning(ProgramInfo programInfo); } }
IListenCall:(监听回调服务接口,主用被心跳监控系统调用)
using System; using System.Collections.Generic; using System.Linq; using System.ServiceModel; using System.Text; namespace ProgramMonitor.Core { public interface IListenCall { [OperationContract] int Listen(string programId); } }
ProgramInfo:(程序信息类,主要用于获取被监控程序的基本信息)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.Runtime.Serialization; namespace ProgramMonitor.Core { [DataContract(Namespace = "http://www.zuowenjun.cn")] public class ProgramInfo { public ProgramInfo() { } [DataMember] public string Id { get; internal set; } [DataMember] public string Name { get; set; } [DataMember] public string Version { get; set; } [DataMember] public string InstalledLocation { get; set; } [DataMember] public string Description { get; set; } private int runState = -1; /// <summary> /// 运行状态,-1:表示停止,0表示启动,1表示运行中 /// </summary> [DataMember] public int RunState { get { return runState; } set { this.UpdateStateTime = DateTime.Now; if (value < 0) { runState = -1; this.StopTime = this.UpdateStateTime; } else if (value == 0) { runState = 0; this.StartTime = this.UpdateStateTime; } else { runState = 1; } } } [DataMember] public DateTime UpdateStateTime { get; private set; } [DataMember] public DateTime StartTime { get; private set; } [DataMember] public DateTime StopTime { get; private set; } } }
ListenClient:(监听客户端类,实现WCF回调服务接口,主要用于被监控的程序)
using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Security.Cryptography; using System.ServiceModel; using System.ServiceModel.Description; using System.Text; namespace ProgramMonitor.Core { public class ListenClient : IListenCall { private static object syncObject = new object(); private static ListenClient instance = null; private ProgramInfo programInfo = null; private string serviceHostAddr = null; private int autoReportRunningInterval = 300; private DuplexChannelFactory<IListenService> listenServiceFactory = null; private IListenService proxyListenService = null; private System.Timers.Timer reportRunningTimer = null; private ListenClient(ProgramInfo programInfo, string serviceHostAddr = null, int autoReportRunningInterval = 300) { programInfo.Id = CreateProgramId(); this.programInfo = programInfo; this.serviceHostAddr = serviceHostAddr; if (autoReportRunningInterval >= 60) //最低1分钟的间隔 { this.autoReportRunningInterval = autoReportRunningInterval; } BuildAutoReportRunningTimer(); } private void BuildAutoReportRunningTimer() { reportRunningTimer = new System.Timers.Timer(autoReportRunningInterval * 1000); reportRunningTimer.Elapsed += (s, e) => { ReportRunning(); }; } private void BuildListenClientService() { if (listenServiceFactory == null) { if (string.IsNullOrEmpty(serviceHostAddr)) { serviceHostAddr = System.Configuration.ConfigurationManager.AppSettings["ServiceHostAddr"]; } InstanceContext instanceContext = new InstanceContext(instance); NetTcpBinding binding = new NetTcpBinding(); binding.ReceiveTimeout = new TimeSpan(0, 5, 0); binding.SendTimeout = new TimeSpan(0, 5, 0); Uri baseAddress = new Uri(string.Format("net.tcp://{0}/ListenService", serviceHostAddr)); listenServiceFactory = new DuplexChannelFactory<IListenService>(instanceContext, binding, new EndpointAddress(baseAddress)); } proxyListenService = listenServiceFactory.CreateChannel(); } public static ListenClient GetInstance(ProgramInfo programInfo, string serviceHostAddr = null, int autoReportRunningInterval = 300) { if (instance == null) { lock (syncObject) { if (instance == null) { instance = new ListenClient(programInfo, serviceHostAddr, autoReportRunningInterval); instance.BuildListenClientService(); } } } return instance; } public void ReportStart() { proxyListenService.Start(programInfo); reportRunningTimer.Start(); } public void ReportStop() { proxyListenService.Stop(programInfo.Id); reportRunningTimer.Stop(); } public void ReportRunning() { try { proxyListenService.ReportRunning(programInfo); } catch { BuildListenClientService(); } } int IListenCall.Listen(string programId) { if (programInfo.Id.Equals(programId, StringComparison.OrdinalIgnoreCase)) { if (programInfo.RunState >= 0) { return 1; } } return -1; } private string CreateProgramId() { Process currProcess = Process.GetCurrentProcess(); int procCount = Process.GetProcessesByName(currProcess.ProcessName).Length; string currentProgramPath = currProcess.MainModule.FileName; return GetMD5HashFromFile(currentProgramPath) + "_" + procCount; } private string GetMD5HashFromFile(string fileName) { try { byte[] hashData = null; using (FileStream fs = new FileStream(fileName, System.IO.FileMode.Open, FileAccess.Read)) { MD5 md5 = new MD5CryptoServiceProvider(); hashData = md5.ComputeHash(fs); fs.Close(); } StringBuilder sb = new StringBuilder(); for (int i = 0; i < hashData.Length; i++) { sb.Append(hashData[i].ToString("x2")); } return sb.ToString(); } catch (Exception ex) { throw new Exception("GetMD5HashFromFile Error:" + ex.Message); } } } }
这里特别说一下:CreateProgramId方法,因为心跳监控系统主要是依据ProgramId来识别每个不同的程序,故ProgramId非常重要,而我这里采用的是文件的 MD5值+进程数作为ProgramId,有些人可能要问,为什么要加进程数呢?原因很简单,因为有些程序是允许开启多个的实例的,如果不加进程数,那么心跳监控系统就无法识别多个同一个程序到底是哪个。
ProgramMonitor
ListenService:(WCF心跳监控服务接口实现类)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ProgramMonitor.Core; using System.ServiceModel; namespace ProgramMonitor.Service { [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.PerCall)] public class ListenService : IListenService { public void Start(ProgramInfo programInfo) { var listenCall = OperationContext.Current.GetCallbackChannel<IListenCall>(); Common.SaveProgramStartInfo(programInfo, listenCall); } public void Stop(string programId) { Common.SaveProgramStopInfo(programId); } public void ReportRunning(ProgramInfo programInfo) { var listenCall = OperationContext.Current.GetCallbackChannel<IListenCall>(); Common.SaveProgramRunningInfo(programInfo, listenCall); } } }
Common:(通用业务逻辑类,主要用于WCF与UI实时沟通与联动)
using System; using System.Collections.Generic; using System.Linq; using System.Text; using ProgramMonitor.Core; using System.Collections.Concurrent; using System.Timers; using System.Threading; using Timer = System.Timers.Timer; using ProgramMonitor.Service; using System.Data.SqlClient; using log4net; namespace ProgramMonitor { public static class Common { public static ConcurrentDictionary<string, ProgramInfo> ProgramInfos = null; public static ConcurrentDictionary<string, IListenCall> ListenCalls = null; public static ConcurrentBag<string> ManualStopProgramIds = null; public static System.Timers.Timer loadTimer = null; public static Timer listenTimer = null; public static SynchronizationContext SyncContext = null; public static Action<ProgramInfo, bool> RefreshListView; public static Action<ProgramInfo, bool> RefreshTabControl; public static int ClearInterval = 5; public static int ListenInterval = 2; public static bool Listening = false; public static string DbConnString = null; public static string[] NoticePhoneNos = null; public static string NoticeWxUserIds = null; public static ILog Logger = LogManager.GetLogger("ProgramMonitor"); public const string SqlProviderName = "System.Data.SqlClient"; public static void SaveProgramStartInfo(ProgramInfo programInfo, IListenCall listenCall) { programInfo.RunState = 0; ProgramInfos.AddOrUpdate(programInfo.Id, programInfo, (key, value) => programInfo); ListenCalls.AddOrUpdate(programInfo.Id, listenCall, (key, value) => listenCall); RefreshListView(programInfo, false); RefreshTabControl(programInfo, true); WriteLog(string.Format("程序名:{0},版本:{1},已启动运行", programInfo.Name, programInfo.Version), false); } public static void SaveProgramStopInfo(string programId) { ProgramInfo programInfo; if (ProgramInfos.TryGetValue(programId, out programInfo)) { programInfo.RunState = -1; RefreshListView(programInfo, false); IListenCall listenCall = null; ListenCalls.TryRemove(programId, out listenCall); RefreshTabControl(programInfo, true); } WriteLog(string.Format("程序名:{0},版本:{1},已停止运行", programInfo.Name, programInfo.Version), false); } public static void SaveProgramRunningInfo(ProgramInfo programInfo, IListenCall listenCall) { if (!ProgramInfos.ContainsKey(programInfo.Id) || !ListenCalls.ContainsKey(programInfo.Id)) { SaveProgramStartInfo(programInfo, listenCall); } programInfo.RunState = 1; RefreshTabControl(programInfo, true); WriteLog(string.Format("程序名:{0},版本:{1},正在运行中", programInfo.Name, programInfo.Version), false); } public static void AutoLoadProgramInfos() { if (loadTimer == null) { loadTimer = new Timer(1 * 60 * 1000); loadTimer.Elapsed += delegate(object sender, ElapsedEventArgs e) { var timer = sender as Timer; try { timer.Stop(); foreach (var item in ProgramInfos) { var programInfo = item.Value; RefreshListView(programInfo, false); } } finally { if (Listening) { timer.Start(); } } }; } else { loadTimer.Interval = 1 * 60 * 1000; } loadTimer.Start(); } public static void AutoListenPrograms() { if (listenTimer == null) { listenTimer = new Timer(ListenInterval * 60 * 1000); listenTimer.Elapsed += delegate(object sender, ElapsedEventArgs e) { var timer = sender as Timer; try { timer.Stop(); foreach (var item in ListenCalls) { bool needUpdateStatInfo = false; var listenCall = item.Value; var programInfo = ProgramInfos[item.Key]; int oldRunState = programInfo.RunState; try { programInfo.RunState = listenCall.Listen(programInfo.Id); } catch { if (programInfo.RunState != -1) { programInfo.RunState = -1; needUpdateStatInfo = true; } } if (programInfo.RunState == -1 && programInfo.StopTime.AddMinutes(5) < DateTime.Now) //如果停了5分钟,则发一次短信 { SendNoticeSms(programInfo); SendNoticeWeiXin(programInfo); programInfo.RunState = -1;//重新刷新状态 } if (oldRunState != programInfo.RunState) { needUpdateStatInfo = true; WriteLog(string.Format("程序名:{0},版本:{1},运行状态变更为:{2}", programInfo.Name, programInfo.Version,programInfo.RunState), false); } RefreshTabControl(programInfo, needUpdateStatInfo); } } finally { if (Listening) { timer.Start(); } } }; } else { listenTimer.Interval = ListenInterval * 60 * 1000; } listenTimer.Start(); } public static void SendNoticeSms(ProgramInfo programInfo) { if (NoticePhoneNos == null || NoticePhoneNos.Length <= 0) return; using (DataAccess da = new DataAccess(Common.DbConnString, Common.SqlProviderName)) { da.UseTransaction(); foreach (string phoneNo in NoticePhoneNos) { var parameters = da.ParameterHelper.AddParameter("@Mbno", phoneNo) .AddParameter("@Msg", string.Format("程序名:{0},版本:{1},安装路径:{2},已停止运行了,请尽快处理!", programInfo.Name, programInfo.Version, programInfo.InstalledLocation)) .AddParameter("@SendTime", DateTime.Now) .AddParameter("@KndType", "监控异常通知") .ToParameterArray(); da.ExecuteCommand("insert into OutBox(Mbno,Msg,SendTime,KndType) values(@Mbno,@Msg,@SendTime,@KndType)", paramObjs: parameters); } da.Commit(); WriteLog(string.Format("程序名:{0},版本:{1},已停止运行超过5分钟,成功发送短信通知到:{2}", programInfo.Name, programInfo.Version, string.Join(",", NoticePhoneNos)), false); } } public static void SendNoticeWeiXin(ProgramInfo programInfo) { if (string.IsNullOrEmpty(NoticeWxUserIds)) return; string msg = string.Format("程序名:{0},版本:{1},安装路径:{2},已停止运行了,请尽快处理!", programInfo.Name, programInfo.Version, programInfo.InstalledLocation); var wx = new WeChat(); var result = wx.SendMessage(NoticeWxUserIds, msg); if (result["errmsg"].ToString().Equals("ok", StringComparison.OrdinalIgnoreCase)) { WriteLog(string.Format("程序名:{0},版本:{1},已停止运行超过5分钟,成功发送微信通知到:{2}", programInfo.Name, programInfo.Version,NoticeWxUserIds), false); } } public static void BuildConnectionString(string server, string db, string uid, string pwd) { SqlConnectionStringBuilder connStrBuilder = new SqlConnectionStringBuilder(); connStrBuilder.DataSource = server; connStrBuilder.InitialCatalog = db; connStrBuilder.UserID = uid; connStrBuilder.Password = pwd; connStrBuilder.IntegratedSecurity = false; connStrBuilder.ConnectTimeout = 15; DbConnString = connStrBuilder.ToString(); } public static void WriteLog(string msg, bool isError = false) { if (isError) { Logger.Error(msg); } else { Logger.Info(msg); } } } }
FrmMain:(心跳监控系统窗体类)
using ProgramMonitor.Core; using ProgramMonitor.Service; using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.ServiceModel; using System.ServiceModel.Description; using System.Text; using System.Threading; using System.Windows.Forms; namespace ProgramMonitor { public partial class FrmMain : Form { private ServiceHost serviceHost = null; public FrmMain() { InitializeComponent(); tabControl1.SizeMode = TabSizeMode.Fixed; tabControl1.ItemSize = new Size(0, 1); #if (DEBUG) btnTestSend.Visible = true; #else btnTestSend.Visible = false; #endif Common.SyncContext = SynchronizationContext.Current; Common.ProgramInfos = new ConcurrentDictionary<string, ProgramInfo>(); Common.ListenCalls = new ConcurrentDictionary<string, IListenCall>(); Common.ManualStopProgramIds = new ConcurrentBag<string>(); Common.RefreshListView = RefreshListView; Common.RefreshTabControl = RefreshTabControl; } #region 自定义方法区域 private void RefreshListView(ProgramInfo programInfo, bool needUpdateStatInfo) { Common.SyncContext.Post(o => { string listViewItemKey = string.Format("lvItem_{0}", programInfo.Id); if (!listView1.Items.ContainsKey(listViewItemKey)) { var lstItem = listView1.Items.Add(listViewItemKey, programInfo.Name, 0); lstItem.Name = listViewItemKey; lstItem.Tag = programInfo.Id; lstItem.SubItems.Add(programInfo.Version); lstItem.SubItems.Add(programInfo.InstalledLocation); lstItem.ToolTipText = programInfo.Description; if (needUpdateStatInfo) { UpdateProgramListenStatInfo(); } } else { if (!Common.ListenCalls.ContainsKey(programInfo.Id) && programInfo.RunState == -1 && Common.ClearInterval > 0 && programInfo.StopTime.AddMinutes(Common.ClearInterval) < DateTime.Now) //当属于正常关闭的程序在指定时间后从监控列表中移除 { RemoveListenItem(programInfo.Id); } } }, null); } private void RefreshTabControl(ProgramInfo programInfo, bool needUpdateStatInfo) { Common.SyncContext.Post(o => { string tabPgName = string.Format("tabpg_{0}", programInfo.Id); string msgCtrlName = string.Format("{0}_MsgText", tabPgName); if (!tabControl1.TabPages.ContainsKey(tabPgName)) { RichTextBox rchTextBox = new RichTextBox(); rchTextBox.Name = msgCtrlName; rchTextBox.Dock = DockStyle.Fill; rchTextBox.ReadOnly = true; AppendTextToRichTextBox(rchTextBox, programInfo); var tabPg = new TabPage(); tabPg.Name = tabPgName; tabPg.Controls.Add(rchTextBox); tabControl1.TabPages.Add(tabPg); } else { var tabPg = tabControl1.TabPages[tabPgName]; var rchTextBox = tabPg.Controls[msgCtrlName] as RichTextBox; AppendTextToRichTextBox(rchTextBox, programInfo); } if (needUpdateStatInfo) { UpdateProgramListenStatInfo(); } }, null); } private void UpdateProgramListenStatInfo() { int runCount = Common.ProgramInfos.Count(p => p.Value.RunState >= 0); labRunCount.Text = string.Format("{0}个", runCount); labStopCount.Text = string.Format("{0}个", Common.ProgramInfos.Count - runCount); foreach (ListViewItem lstItem in listView1.Items) { string programId = lstItem.Tag.ToString(); if (Common.ProgramInfos[programId].RunState == -1) { lstItem.ForeColor = Color.Red; } else { lstItem.ForeColor = Color.Black; } } } private void RemoveListenItem(string programInfoId) { ProgramInfo programInfo = Common.ProgramInfos[programInfoId]; listView1.Items.RemoveByKey(string.Format("lvItem_{0}", programInfo.Id)); tabControl1.TabPages.RemoveByKey(string.Format("tabpg_{0}", programInfo.Id)); Common.ProgramInfos.TryRemove(programInfo.Id, out programInfo); IListenCall listenCall = null; Common.ListenCalls.TryRemove(programInfoId, out listenCall); UpdateProgramListenStatInfo(); } private void AppendTextToRichTextBox(RichTextBox rchTextBox, Core.ProgramInfo programInfo) { Color msgColor = Color.Black; string lineMsg = string.Format("{0:yyyy-MM-dd HH:mm}\t{1}({2})\t{3} \n", DateTime.Now, programInfo.Name, programInfo.Version, GetRunStateString(programInfo.RunState, out msgColor)); rchTextBox.SelectionColor = msgColor; rchTextBox.SelectionStart = rchTextBox.Text.Length; rchTextBox.AppendText(lineMsg); rchTextBox.SelectionLength = rchTextBox.Text.Length; if(rchTextBox.Lines.Length>1000) { } } private string GetRunStateString(int runState, out Color msgColor) { if (runState < 0) { msgColor = Color.Red; return "程序已停止运行"; } else if (runState == 0) { msgColor = Color.Blue; return "程序已启动运行"; } else { msgColor = Color.Black; return "程序已在运行中"; } } private void StartListenService() { if (serviceHost == null) { string serviceHostAddr = System.Configuration.ConfigurationManager.AppSettings["ServiceHostAddr"]; string serviceMetaHostAddr = System.Configuration.ConfigurationManager.AppSettings["ServiceMetaHostAddr"]; serviceHost = new ServiceHost(typeof(ListenService)); NetTcpBinding binding = new NetTcpBinding(); binding.ReceiveTimeout = new TimeSpan(0, 5, 0); binding.SendTimeout = new TimeSpan(0, 5, 0); serviceHost.AddServiceEndpoint(typeof(IListenService), binding, string.Format("net.tcp://{0}/ListenService", serviceHostAddr)); if (serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>() == null) { ServiceMetadataBehavior behavior = new ServiceMetadataBehavior(); behavior.HttpGetEnabled = true; behavior.HttpGetUrl = new Uri(string.Format("http://{0}/ListenService/metadata", serviceMetaHostAddr)); serviceHost.Description.Behaviors.Add(behavior); } serviceHost.Opened += (s, arg) => { SetUIStyle("S"); Common.Listening = true; Common.AutoLoadProgramInfos(); Common.AutoListenPrograms(); }; serviceHost.Closed += (s, arg) => { SetUIStyle("C"); Common.loadTimer.Stop(); Common.listenTimer.Stop(); Common.Listening = false; }; } serviceHost.Open(); } private void StopListenService() { try { if (serviceHost != null && serviceHost.State != CommunicationState.Closed) { serviceHost.Close(); } serviceHost = null; } catch { } } private void SetUIStyle(string state) { if (state == "S") { labSericeState.BackColor = Color.Green; txtRefreshInterval.Enabled = false; txtListenInterval.Enabled = false; btnExec.Tag = "C"; btnExec.Text = "停止监控"; panel1.Enabled = false; panel2.Enabled = false; } else { labSericeState.BackColor = Color.Red; txtRefreshInterval.Enabled = true; txtListenInterval.Enabled = true; btnExec.Tag = "S"; btnExec.Text = "开启监控"; panel1.Enabled = true; panel2.Enabled = true; } } private void InitListViewStyle() { ImageList imgList = new ImageList(); imgList.ImageSize = new Size(32, 32); imgList.Images.Add(Properties.Resources.monitor); listView1.SmallImageList = imgList; listView1.LargeImageList = imgList; listView1.View = View.Details; listView1.GridLines = false; listView1.FullRowSelect = true; listView1.Columns.Add("程序名称", -2); listView1.Columns.Add("版本"); listView1.Columns.Add("运行路径"); int avgWidth = listView1.Width / 3; } private void InitNoticeSetting() { if (chkSendSms.Checked) { bool dbConnected = false; Common.BuildConnectionString(txtServer.Text, txtDb.Text, txtUID.Text, txtPwd.Text); using (var da = new DataAccess(Common.DbConnString, Common.SqlProviderName)) { try { da.ExecuteScalar<DateTime>("select getdate()"); dbConnected = true; } catch (Exception ex) { MessageBox.Show("数据库测试连接失败,原因:" + ex.Message); } } if (dbConnected) { if (txtPhoneNos.Text.Trim().IndexOf(",") >= 0) { Common.NoticePhoneNos = txtPhoneNos.Text.Trim().Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries); } else { Common.NoticePhoneNos = new[] { txtPhoneNos.Text.Trim() }; } } } else { Common.NoticePhoneNos = null; } if (chkSendWx.Checked) { Common.NoticeWxUserIds = txtWxUIDs.Text.Trim(); } else { Common.NoticeWxUserIds = null; } } private bool IsRightClickSelectedItem(Point point) { foreach (ListViewItem item in listView1.SelectedItems) { if (item.Bounds.Contains(point)) { return true; } } return false; } #endregion private void btnExec_Click(object sender, EventArgs e) { string state = (btnExec.Tag ?? "S").ToString(); if (state == "S") { InitNoticeSetting(); Common.ClearInterval = int.Parse(txtRefreshInterval.Text); Common.ListenInterval = int.Parse(txtListenInterval.Text); StartListenService(); } else { StopListenService(); } } private void listView1_SelectedIndexChanged(object sender, EventArgs e) { if (listView1.SelectedItems.Count <= 0) return; string programId = listView1.SelectedItems[0].Tag.ToString(); string tabPgName = string.Format("tabpg_{0}", programId); if (tabControl1.TabPages.ContainsKey(tabPgName)) { tabControl1.SelectedTab = tabControl1.TabPages[tabPgName]; } else { MessageBox.Show("未找到相应的程序监控记录!"); listView1.SelectedItems[0].ForeColor = Color.Red; } } private void FrmMain_Load(object sender, EventArgs e) { InitListViewStyle(); } private void FrmMain_FormClosing(object sender, FormClosingEventArgs e) { if (MessageBox.Show("您确定要退出吗?退出后将无法正常监控各程序的运行状况", "退出提示", MessageBoxButtons.OKCancel, MessageBoxIcon.Question) == DialogResult.Cancel) { e.Cancel = true; return; } StopListenService(); } private void btnTestSend_Click(object sender, EventArgs e) { var wx = new WeChat(); var msg = wx.SendMessage("kyezuo", "测试消息,有程序停止运行了,赶紧处理!"); MessageBox.Show(msg["errmsg"].ToString()); } private void listView1_MouseUp(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right && IsRightClickSelectedItem(e.Location)) { ctxMuPop.Show(listView1, e.Location); } } private void removeToolStripMenuItem_Click(object sender, EventArgs e) { if (listView1.SelectedItems.Count <= 0) return; string programId = listView1.SelectedItems[0].Tag.ToString(); if (Common.ProgramInfos[programId].RunState != -1) { MessageBox.Show("只有被监控的程序处于已停止状态的监控项才能移除,除外情况请务必保持正常!"); return; } RemoveListenItem(programId); } } }
窗体类中主要是用到了几个更新UI上控件信息的方法以及开启、关闭WCF服务的方法,很简单,一看就明白,无需多讲。
FrmMain.Designer.cs:(Form窗体设计类,系统自动生成的,再此贴出是便于大家可以直接COPY到自己的代码中直接用)
namespace ProgramMonitor { partial class FrmMain { /// <summary> /// 必需的设计器变量。 /// </summary> private System.ComponentModel.IContainer components = null; /// <summary> /// 清理所有正在使用的资源。 /// </summary> /// <param name="disposing">如果应释放托管资源,为 true;否则为 false。</param> protected override void Dispose(bool disposing) { if (disposing && (components != null)) { components.Dispose(); } base.Dispose(disposing); } #region Windows 窗体设计器生成的代码 /// <summary> /// 设计器支持所需的方法 - 不要 /// 使用代码编辑器修改此方法的内容。 /// </summary> private void InitializeComponent() { this.components = new System.ComponentModel.Container(); this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.listView1 = new System.Windows.Forms.ListView(); this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabControl2 = new System.Windows.Forms.TabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); this.groupBox1 = new System.Windows.Forms.GroupBox(); this.btnTestSend = new System.Windows.Forms.Button(); this.labSericeState = new System.Windows.Forms.Label(); this.btnExec = new System.Windows.Forms.Button(); this.txtListenInterval = new System.Windows.Forms.TextBox(); this.txtRefreshInterval = new System.Windows.Forms.TextBox(); this.labStopCount = new System.Windows.Forms.Label(); this.labRunCount = new System.Windows.Forms.Label(); this.label4 = new System.Windows.Forms.Label(); this.label3 = new System.Windows.Forms.Label(); this.label2 = new System.Windows.Forms.Label(); this.label1 = new System.Windows.Forms.Label(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.labLine = new System.Windows.Forms.Label(); this.panel1 = new System.Windows.Forms.Panel(); this.txtPwd = new System.Windows.Forms.TextBox(); this.txtUID = new System.Windows.Forms.TextBox(); this.txtDb = new System.Windows.Forms.TextBox(); this.txtServer = new System.Windows.Forms.TextBox(); this.label8 = new System.Windows.Forms.Label(); this.label9 = new System.Windows.Forms.Label(); this.txtPhoneNos = new System.Windows.Forms.TextBox(); this.label7 = new System.Windows.Forms.Label(); this.label6 = new System.Windows.Forms.Label(); this.label5 = new System.Windows.Forms.Label(); this.chkSendSms = new System.Windows.Forms.CheckBox(); this.panel2 = new System.Windows.Forms.Panel(); this.txtWxUIDs = new System.Windows.Forms.TextBox(); this.chkSendWx = new System.Windows.Forms.CheckBox(); this.label10 = new System.Windows.Forms.Label(); this.ctxMuPop = new System.Windows.Forms.ContextMenuStrip(this.components); this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.tableLayoutPanel1.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); this.splitContainer1.Panel1.SuspendLayout(); this.splitContainer1.Panel2.SuspendLayout(); this.splitContainer1.SuspendLayout(); this.tabControl2.SuspendLayout(); this.tabPage1.SuspendLayout(); this.groupBox1.SuspendLayout(); this.tabPage2.SuspendLayout(); this.panel1.SuspendLayout(); this.panel2.SuspendLayout(); this.ctxMuPop.SuspendLayout(); this.SuspendLayout(); // // tableLayoutPanel1 // this.tableLayoutPanel1.ColumnCount = 1; this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); this.tableLayoutPanel1.Controls.Add(this.splitContainer1, 0, 1); this.tableLayoutPanel1.Controls.Add(this.tabControl2, 0, 0); this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill; this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0); this.tableLayoutPanel1.Name = "tableLayoutPanel1"; this.tableLayoutPanel1.RowCount = 2; this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 150F)); this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); this.tableLayoutPanel1.Size = new System.Drawing.Size(1039, 722); this.tableLayoutPanel1.TabIndex = 0; // // splitContainer1 // this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; this.splitContainer1.FixedPanel = System.Windows.Forms.FixedPanel.Panel1; this.splitContainer1.Location = new System.Drawing.Point(3, 153); this.splitContainer1.Name = "splitContainer1"; // // splitContainer1.Panel1 // this.splitContainer1.Panel1.AutoScroll = true; this.splitContainer1.Panel1.Controls.Add(this.listView1); this.splitContainer1.Panel1MinSize = 150; // // splitContainer1.Panel2 // this.splitContainer1.Panel2.Controls.Add(this.tabControl1); this.splitContainer1.Panel2MinSize = 500; this.splitContainer1.Size = new System.Drawing.Size(1033, 608); this.splitContainer1.SplitterDistance = 296; this.splitContainer1.TabIndex = 0; // // listView1 // this.listView1.Dock = System.Windows.Forms.DockStyle.Fill; this.listView1.Location = new System.Drawing.Point(0, 0); this.listView1.Name = "listView1"; this.listView1.Size = new System.Drawing.Size(296, 608); this.listView1.TabIndex = 0; this.listView1.UseCompatibleStateImageBehavior = false; this.listView1.View = System.Windows.Forms.View.Details; this.listView1.SelectedIndexChanged += new System.EventHandler(this.listView1_SelectedIndexChanged); this.listView1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.listView1_MouseUp); // // tabControl1 // this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl1.Location = new System.Drawing.Point(0, 0); this.tabControl1.Margin = new System.Windows.Forms.Padding(3, 0, 3, 3); this.tabControl1.Name = "tabControl1"; this.tabControl1.SelectedIndex = 0; this.tabControl1.Size = new System.Drawing.Size(733, 608); this.tabControl1.TabIndex = 0; // // tabControl2 // this.tabControl2.Controls.Add(this.tabPage1); this.tabControl2.Controls.Add(this.tabPage2); this.tabControl2.Dock = System.Windows.Forms.DockStyle.Fill; this.tabControl2.Location = new System.Drawing.Point(3, 3); this.tabControl2.Name = "tabControl2"; this.tabControl2.SelectedIndex = 0; this.tabControl2.Size = new System.Drawing.Size(1033, 144); this.tabControl2.TabIndex = 1; // // tabPage1 // this.tabPage1.Controls.Add(this.groupBox1); this.tabPage1.Location = new System.Drawing.Point(4, 22); this.tabPage1.Name = "tabPage1"; this.tabPage1.Padding = new System.Windows.Forms.Padding(3); this.tabPage1.Size = new System.Drawing.Size(1025, 118); this.tabPage1.TabIndex = 0; this.tabPage1.Text = "监控基本信息"; this.tabPage1.UseVisualStyleBackColor = true; // // groupBox1 // this.groupBox1.Controls.Add(this.btnTestSend); this.groupBox1.Controls.Add(this.labSericeState); this.groupBox1.Controls.Add(this.btnExec); this.groupBox1.Controls.Add(this.txtListenInterval); this.groupBox1.Controls.Add(this.txtRefreshInterval); this.groupBox1.Controls.Add(this.labStopCount); this.groupBox1.Controls.Add(this.labRunCount); this.groupBox1.Controls.Add(this.label4); this.groupBox1.Controls.Add(this.label3); this.groupBox1.Controls.Add(this.label2); this.groupBox1.Controls.Add(this.label1); this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; this.groupBox1.Location = new System.Drawing.Point(3, 3); this.groupBox1.Name = "groupBox1"; this.groupBox1.Size = new System.Drawing.Size(1019, 112); this.groupBox1.TabIndex = 1; this.groupBox1.TabStop = false; // // btnTestSend // this.btnTestSend.Location = new System.Drawing.Point(14, 73); this.btnTestSend.Name = "btnTestSend"; this.btnTestSend.Size = new System.Drawing.Size(123, 23); this.btnTestSend.TabIndex = 4; this.btnTestSend.Text = "测试发送消息"; this.btnTestSend.UseVisualStyleBackColor = true; this.btnTestSend.Click += new System.EventHandler(this.btnTestSend_Click); // // labSericeState // this.labSericeState.BackColor = System.Drawing.Color.Red; this.labSericeState.Location = new System.Drawing.Point(578, 40); this.labSericeState.Name = "labSericeState"; this.labSericeState.Size = new System.Drawing.Size(34, 22); this.labSericeState.TabIndex = 3; this.labSericeState.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; // // btnExec // this.btnExec.Location = new System.Drawing.Point(487, 40); this.btnExec.Name = "btnExec"; this.btnExec.Size = new System.Drawing.Size(75, 23); this.btnExec.TabIndex = 2; this.btnExec.Text = "开启监控"; this.btnExec.UseVisualStyleBackColor = true; this.btnExec.Click += new System.EventHandler(this.btnExec_Click); // // txtListenInterval // this.txtListenInterval.Location = new System.Drawing.Point(147, 41); this.txtListenInterval.Name = "txtListenInterval"; this.txtListenInterval.Size = new System.Drawing.Size(68, 21); this.txtListenInterval.TabIndex = 1; this.txtListenInterval.Text = "2"; // // txtRefreshInterval // this.txtRefreshInterval.Location = new System.Drawing.Point(411, 41); this.txtRefreshInterval.Name = "txtRefreshInterval"; this.txtRefreshInterval.Size = new System.Drawing.Size(68, 21); this.txtRefreshInterval.TabIndex = 1; this.txtRefreshInterval.Text = "5"; // // labStopCount // this.labStopCount.AutoSize = true; this.labStopCount.Font = new System.Drawing.Font("宋体", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.labStopCount.ForeColor = System.Drawing.Color.Red; this.labStopCount.Location = new System.Drawing.Point(841, 42); this.labStopCount.Name = "labStopCount"; this.labStopCount.Size = new System.Drawing.Size(40, 19); this.labStopCount.TabIndex = 0; this.labStopCount.Text = "0个"; // // labRunCount // this.labRunCount.AutoSize = true; this.labRunCount.Font = new System.Drawing.Font("宋体", 14.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(134))); this.labRunCount.ForeColor = System.Drawing.Color.Green; this.labRunCount.Location = new System.Drawing.Point(721, 42); this.labRunCount.Name = "labRunCount"; this.labRunCount.Size = new System.Drawing.Size(40, 19); this.labRunCount.TabIndex = 0; this.labRunCount.Text = "0个"; // // label4 // this.label4.AutoSize = true; this.label4.Location = new System.Drawing.Point(782, 45); this.label4.Name = "label4"; this.label4.Size = new System.Drawing.Size(53, 12); this.label4.TabIndex = 0; this.label4.Text = "已停止:"; // // label3 // this.label3.AutoSize = true; this.label3.Location = new System.Drawing.Point(645, 45); this.label3.Name = "label3"; this.label3.Size = new System.Drawing.Size(53, 12); this.label3.TabIndex = 0; this.label3.Text = "运行中:"; // // label2 // this.label2.AutoSize = true; this.label2.Location = new System.Drawing.Point(12, 45); this.label2.Name = "label2"; this.label2.Size = new System.Drawing.Size(125, 12); this.label2.TabIndex = 0; this.label2.Text = "监听探测间隔(分):"; // // label1 // this.label1.AutoSize = true; this.label1.Location = new System.Drawing.Point(232, 45); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(173, 12); this.label1.TabIndex = 0; this.label1.Text = "正常停止清除列表时长(分):"; // // tabPage2 // this.tabPage2.Controls.Add(this.labLine); this.tabPage2.Controls.Add(this.panel1); this.tabPage2.Controls.Add(this.panel2); this.tabPage2.Location = new System.Drawing.Point(4, 22); this.tabPage2.Name = "tabPage2"; this.tabPage2.Padding = new System.Windows.Forms.Padding(3); this.tabPage2.Size = new System.Drawing.Size(1025, 118); this.tabPage2.TabIndex = 1; this.tabPage2.Text = "消息通知设置"; this.tabPage2.UseVisualStyleBackColor = true; // // labLine // this.labLine.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(224)))), ((int)(((byte)(224)))), ((int)(((byte)(224))))); this.labLine.Dock = System.Windows.Forms.DockStyle.Bottom; this.labLine.FlatStyle = System.Windows.Forms.FlatStyle.Popup; this.labLine.Location = new System.Drawing.Point(3, 59); this.labLine.Name = "labLine"; this.labLine.Size = new System.Drawing.Size(1019, 2); this.labLine.TabIndex = 0; // // panel1 // this.panel1.Controls.Add(this.txtPwd); this.panel1.Controls.Add(this.txtUID); this.panel1.Controls.Add(this.txtDb); this.panel1.Controls.Add(this.txtServer); this.panel1.Controls.Add(this.label8); this.panel1.Controls.Add(this.label9); this.panel1.Controls.Add(this.txtPhoneNos); this.panel1.Controls.Add(this.label7); this.panel1.Controls.Add(this.label6); this.panel1.Controls.Add(this.label5); this.panel1.Controls.Add(this.chkSendSms); this.panel1.Dock = System.Windows.Forms.DockStyle.Top; this.panel1.Location = new System.Drawing.Point(3, 3); this.panel1.Name = "panel1"; this.panel1.Size = new System.Drawing.Size(1019, 52); this.panel1.TabIndex = 0; // // txtPwd // this.txtPwd.Location = new System.Drawing.Point(925, 24); this.txtPwd.Name = "txtPwd"; this.txtPwd.PasswordChar = '*'; this.txtPwd.Size = new System.Drawing.Size(91, 21); this.txtPwd.TabIndex = 5; // // txtUID // this.txtUID.Location = new System.Drawing.Point(779, 24); this.txtUID.Name = "txtUID"; this.txtUID.PasswordChar = '*'; this.txtUID.Size = new System.Drawing.Size(91, 21); this.txtUID.TabIndex = 4; // // txtDb // this.txtDb.Location = new System.Drawing.Point(628, 24); this.txtDb.Name = "txtDb"; this.txtDb.Size = new System.Drawing.Size(96, 21); this.txtDb.TabIndex = 3; this.txtDb.Text = ""; // // txtServer // this.txtServer.Location = new System.Drawing.Point(423, 24); this.txtServer.Name = "txtServer"; this.txtServer.Size = new System.Drawing.Size(150, 21); this.txtServer.TabIndex = 2; this.txtServer.Text = ""; // // label8 // this.label8.AutoSize = true; this.label8.Location = new System.Drawing.Point(876, 27); this.label8.Name = "label8"; this.label8.Size = new System.Drawing.Size(41, 12); this.label8.TabIndex = 7; this.label8.Text = "密码:"; // // label9 // this.label9.AutoSize = true; this.label9.Location = new System.Drawing.Point(579, 27); this.label9.Name = "label9"; this.label9.Size = new System.Drawing.Size(53, 12); this.label9.TabIndex = 7; this.label9.Text = "数据库:"; // // txtPhoneNos // this.txtPhoneNos.Location = new System.Drawing.Point(213, 22); this.txtPhoneNos.Name = "txtPhoneNos"; this.txtPhoneNos.Size = new System.Drawing.Size(155, 21); this.txtPhoneNos.TabIndex = 1; // // label7 // this.label7.AutoSize = true; this.label7.Location = new System.Drawing.Point(730, 27); this.label7.Name = "label7"; this.label7.Size = new System.Drawing.Size(53, 12); this.label7.TabIndex = 7; this.label7.Text = "用户名:"; // // label6 // this.label6.AutoSize = true; this.label6.Location = new System.Drawing.Point(374, 27); this.label6.Name = "label6"; this.label6.Size = new System.Drawing.Size(53, 12); this.label6.TabIndex = 7; this.label6.Text = "服务器:"; // // label5 // this.label5.AutoSize = true; this.label5.Location = new System.Drawing.Point(3, 27); this.label5.Name = "label5"; this.label5.Size = new System.Drawing.Size(185, 12); this.label5.TabIndex = 7; this.label5.Text = "通知手机号(多个请用,分隔):"; // // chkSendSms // this.chkSendSms.AutoSize = true; this.chkSendSms.Location = new System.Drawing.Point(3, 3); this.chkSendSms.Name = "chkSendSms"; this.chkSendSms.Size = new System.Drawing.Size(72, 16); this.chkSendSms.TabIndex = 0; this.chkSendSms.Text = "短信通知"; this.chkSendSms.UseVisualStyleBackColor = true; // // panel2 // this.panel2.Controls.Add(this.txtWxUIDs); this.panel2.Controls.Add(this.chkSendWx); this.panel2.Controls.Add(this.label10); this.panel2.Dock = System.Windows.Forms.DockStyle.Bottom; this.panel2.Location = new System.Drawing.Point(3, 61); this.panel2.Name = "panel2"; this.panel2.Size = new System.Drawing.Size(1019, 54); this.panel2.TabIndex = 1; // // txtWxUIDs // this.txtWxUIDs.Location = new System.Drawing.Point(188, 15); this.txtWxUIDs.Name = "txtWxUIDs"; this.txtWxUIDs.Size = new System.Drawing.Size(828, 21); this.txtWxUIDs.TabIndex = 0; this.txtWxUIDs.Text = "@all"; // // chkSendWx // this.chkSendWx.AutoSize = true; this.chkSendWx.Location = new System.Drawing.Point(3, 3); this.chkSendWx.Name = "chkSendWx"; this.chkSendWx.Size = new System.Drawing.Size(72, 16); this.chkSendWx.TabIndex = 6; this.chkSendWx.Text = "微信通知"; this.chkSendWx.UseVisualStyleBackColor = true; // // label10 // this.label10.AutoSize = true; this.label10.Location = new System.Drawing.Point(3, 24); this.label10.Name = "label10"; this.label10.Size = new System.Drawing.Size(179, 12); this.label10.TabIndex = 7; this.label10.Text = "通知微信ID(多个请用|分隔):"; // // ctxMuPop // this.ctxMuPop.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.removeToolStripMenuItem}); this.ctxMuPop.Name = "ctxMuPop"; this.ctxMuPop.Size = new System.Drawing.Size(125, 26); // // removeToolStripMenuItem // this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; this.removeToolStripMenuItem.Size = new System.Drawing.Size(124, 22); this.removeToolStripMenuItem.Text = "移除监控"; this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); // // FrmMain // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1039, 722); this.Controls.Add(this.tableLayoutPanel1); this.Name = "FrmMain"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; this.Text = "程序运行状态监控系统"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.FrmMain_FormClosing); this.Load += new System.EventHandler(this.FrmMain_Load); this.tableLayoutPanel1.ResumeLayout(false); this.splitContainer1.Panel1.ResumeLayout(false); this.splitContainer1.Panel2.ResumeLayout(false); ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); this.splitContainer1.ResumeLayout(false); this.tabControl2.ResumeLayout(false); this.tabPage1.ResumeLayout(false); this.groupBox1.ResumeLayout(false); this.groupBox1.PerformLayout(); this.tabPage2.ResumeLayout(false); this.panel1.ResumeLayout(false); this.panel1.PerformLayout(); this.panel2.ResumeLayout(false); this.panel2.PerformLayout(); this.ctxMuPop.ResumeLayout(false); this.ResumeLayout(false); } #endregion private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; private System.Windows.Forms.SplitContainer splitContainer1; private System.Windows.Forms.ListView listView1; private System.Windows.Forms.GroupBox groupBox1; private System.Windows.Forms.TextBox txtListenInterval; private System.Windows.Forms.Label label2; private System.Windows.Forms.Label labStopCount; private System.Windows.Forms.Label labRunCount; private System.Windows.Forms.Label label4; private System.Windows.Forms.Label label3; private System.Windows.Forms.Button btnExec; private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.Label labSericeState; private System.Windows.Forms.TextBox txtRefreshInterval; private System.Windows.Forms.Label label1; private System.Windows.Forms.TabControl tabControl2; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage2; private System.Windows.Forms.Panel panel2; private System.Windows.Forms.CheckBox chkSendWx; private System.Windows.Forms.Label label10; private System.Windows.Forms.TextBox txtWxUIDs; private System.Windows.Forms.Panel panel1; private System.Windows.Forms.TextBox txtPwd; private System.Windows.Forms.TextBox txtUID; private System.Windows.Forms.TextBox txtDb; private System.Windows.Forms.TextBox txtServer; private System.Windows.Forms.Label label8; private System.Windows.Forms.Label label9; private System.Windows.Forms.TextBox txtPhoneNos; private System.Windows.Forms.Label label7; private System.Windows.Forms.Label label6; private System.Windows.Forms.Label label5; private System.Windows.Forms.CheckBox chkSendSms; private System.Windows.Forms.Label labLine; private System.Windows.Forms.Button btnTestSend; private System.Windows.Forms.ContextMenuStrip ctxMuPop; private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem; } }
被监控的客户端程序集成WCF监听客户端很简单,只需引用ProgramMonitor.Core,然后实例化ListenClient,最后就可以通过该ListenClient与心跳监控系统进行双工通讯,在此就不贴出源代码了。
上述代码中还有用到两个类:
DataAccess:数据访问类,这个我之前的文章有介绍,详见:DataAccess通用数据库访问类,简单易用,功能强悍
WeChat:微信企业号发送消息类,注意是微信企业号,不是公众号,这里我也贴出源代码来,供大家了解:
using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Web; namespace ProgramMonitor.Service { public class WeChat { private readonly string _url = null; private readonly string _corpid = null; private readonly string _secret = null; public WeChat() { _url = "https://qyapi.weixin.qq.com/cgi-bin"; _corpid = "CorpID是企业号的标识,每个企业号拥有一个唯一的CorpID"; _secret = "secret是管理组凭证密钥,系统管理员在企业号管理后台创建管理组时,企业号后台为该管理组分配一个唯一的secret"; } public string GetToken(string url_prefix = "/") { string urlParams = string.Format("corpid={0}&corpsecret={1}", HttpUtility.UrlEncodeUnicode(_corpid), HttpUtility.UrlEncodeUnicode(_secret)); string url = _url + url_prefix + "gettoken?" + urlParams; string result = HttpGet(url); var content = JObject.Parse(result); return content["access_token"].ToString(); } public JObject PostData(dynamic data, string url_prefix = "/") { string dataStr = JsonConvert.SerializeObject(data); string url = _url + url_prefix + "message/send?access_token=" + GetToken(); string result = HttpPost(url, dataStr); return JObject.Parse(result); } public JObject SendMessage(string touser, string message) { var data = new { touser = touser, toparty = "1", msgtype = "text", agentid = "2", text = new { content = message }, safe = "0" }; var jResult = PostData(data); return jResult; } private string HttpPost(string Url, string postDataStr) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url); request.Method = "POST"; request.ContentType = "application/x-www-form-urlencoded"; byte[] data = Encoding.UTF8.GetBytes(postDataStr); request.ContentLength = data.Length; Stream myRequestStream = request.GetRequestStream(); myRequestStream.Write(data, 0, data.Length); myRequestStream.Close(); HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream myResponseStream = response.GetResponseStream(); StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8); string retString = myStreamReader.ReadToEnd(); myStreamReader.Close(); myResponseStream.Close(); return retString; } public string HttpGet(string Url, string urlParams = null) { HttpWebRequest request = (HttpWebRequest)WebRequest.Create(Url + (string.IsNullOrEmpty(urlParams) ? "" : "?") + urlParams); request.Method = "GET"; request.ContentType = "text/html;charset=UTF-8"; HttpWebResponse response = (HttpWebResponse)request.GetResponse(); Stream myResponseStream = response.GetResponseStream(); StreamReader myStreamReader = new StreamReader(myResponseStream, Encoding.UTF8); string retString = myStreamReader.ReadToEnd(); myStreamReader.Close(); myResponseStream.Close(); return retString; } } }
具体的关于微信企业号开发文档,可参见:http://qydev.weixin.qq.com/wiki/index.php
最后的效果如下:
心跳监控程序监控效果:
手机收到异常消息:
(《-这是企业号发出的消息) (《-这里短信消息,当然发短信是我公司的平台接口发出的,发短信是需要RMB的,故不建议)
好了本文就到此结束,可能功能相对简单,还有一些不足,欢迎大家评论交流,谢谢!