一个winform程序中对窗体假死问题进行日志记录的控件

本文就是记录一个winform控件的开发过程,这个控件能实时监控客户机出现窗体假死的情况,并且给予记录日志中。

先贴出原始代码

using Microsoft.Diagnostics.Runtime;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using UPPERIOC2.UPPER.EmailErrorSender.Sender;
using UPPERIOC2.UPPER.Util;

namespace FrmControl.C
{
	public class SystemStopwatch: UserControl
	{
		TempForm te = new TempForm();
		CancellationToken can = new CancellationToken();
		private int mainThreadId;
		private Thread mainThread;

		public SystemStopwatch() {
			/*BackgroundImage = Properties.Resources.实验室监控;
			BackgroundImageLayout = ImageLayout.Stretch;
			te.MouseLeave += MouseleaveD;
			this.MouseEnter += MouseEnterD;*/
			this.Visible = false;
			mainThread = Thread.CurrentThread;
			mainThreadId = Thread.CurrentThread.ManagedThreadId;
			Task.Factory.StartNew(() => {
				while (this.FindForm()!= null && !this.FindForm().Visible)
				{
					Thread.Sleep(20);
				}
				
				while (true)
				{
					try
					{

						StartAnewStopwatch();
					}
					catch (Exception ex)
					{
						Console.WriteLine(ex.Message +ex.StackTrace);
					}
				}
			},can);
		}
		
		private void StartAnewStopwatch()
		{
			MyStopwatch sw = MyStopwatch.StartNew();
			Thread.Sleep(300);

			var ta = this.BeginInvoke(new Action(() =>
			{
				sw.Stop();
			}));
			ta.AsyncWaitHandle.WaitOne(10000);
			//bool r = Task.WaitAll(new Task[] { ta },10000);
				sw.Dispose();
			if (sw.GetAdd() > 9000 )
			{
				var re = PrintStackTrace(Process.GetCurrentProcess().Id);
				re = "应用不正常响应,堆栈信息在" + re;
				if (EmailSender.instance == null)
				{
					Show("错误邮件未配置。堆栈死机。"+re,"提示");
				}
				EmailSender.instance?.SendEmail(re);
			}
			else
			{

			}

		}
		[DllImport("user32.dll", SetLastError = true)]
		private static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

		[DllImport("user32.dll")]
		private static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

		private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
		private const uint SWP_NOMOVE = 0x0002;
		private const uint SWP_NOSIZE = 0x0001;
		private const uint SWP_SHOWWINDOW = 0x0040;

		public static DialogResult Show(string message, string title)
		{
			DialogResult result = MessageBox.Show(message, title);

			// 获取 MessageBox 窗口句柄
			IntPtr hWnd = FindWindow(null, title);
			if (hWnd != IntPtr.Zero)
			{
				SetWindowPos(hWnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW);
			}

			return result;
		}
		public string PrintStackTrace(int processId)
		{
			 DataTarget target = DataTarget.AttachToProcess(processId, 3000, AttachFlag.Passive);
			System.Threading.Thread.Sleep(3000); // 等待数据收集
			
			ClrRuntime runtime = target.ClrVersions[0].CreateRuntime();

			foreach (var thread in runtime.Threads)
			{
				if (thread.ManagedThreadId != mainThreadId) continue;

				Console.WriteLine($"Thread {thread.ManagedThreadId:X}:");
				var str = "";

				foreach (var frame in thread.StackTrace)
				{
					str += ($"{frame}");
				}
				return str;
			}
			return "";
		}
		protected override void DestroyHandle()
		{
			base.DestroyHandle();
			can.ThrowIfCancellationRequested();
		}
	
	}

	
}

但是实际使用的过程中发现,控件在debug的时候依旧会弹窗,这边我考虑了两个优化方向,一个是通过重写stopwatch逻辑优化计时逻辑,从而达到debug不弹窗,第二个是使用日志代替弹窗。日志代替比较简单,我们就不演示了,我们试一下重写stopwatch

Stopwatch方法不是虚方法...再实现一个把

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace UPPERIOC2.UPPER.Util
{
	public class MyStopwatch : IDisposable
	{
		private bool Run = true;
		private bool start = false;
		protected volatile int ElapsedMilliseconds = 0;
		private CancellationToken can = new CancellationToken();
		public int GetAdd(int value = 0) {
			lock (this)
			{
				return ElapsedMilliseconds += value;
			}
		}
		public MyStopwatch() {
			Task.Factory.StartNew(() => {
				while (Run)
				{
					DateTime st = DateTime.Now;
					while (start)
					{
						//st = ;
						st = DateTime.Now;

						Thread.Sleep(500);
						if ((DateTime.Now - st).TotalMilliseconds < 600)
						{
							GetAdd(501);
						}
						Console.WriteLine((DateTime.Now - st).TotalMilliseconds);
					}
					Thread.Yield();
				}
			},can);
		}
		public void Start() {
			start = true;
		}
		public void Stop()
		{
			start = false;
			can.ThrowIfCancellationRequested();
		}
		public static MyStopwatch StartNew()
		{
			var r = new MyStopwatch();
			r.Start();
			return r;
		}

		public void Dispose()
		{
			Run = false;

		}
	}
}

一个不精确但是可以防止debug的计时器

 代码上传到了我的开源项目里面王世沛/UpperComAutoTesticon-default.png?t=O83Ahttps://gitee.com/laowangm_m/upper-com-auto-test.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码农门卫部的王先生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值