C# 监控服务启动窗体进程

1、实现服务进程启动窗体进程

2、代码如下:

namespace AppMonitor.Model
{
    /// <summary>
    /// 应用信息
    /// </summary>
    public class AppInfo
    {
        /// <summary>
        /// sesssion名称
        /// </summary>
        [XmlElement()]
        public string WinStationName { get; set; }

        [XmlElementAttribute()]
        public List<AppInfoDetail> Item;
    }

    public class AppInfoDetail
    {        
        /// <summary>
        /// 会话Id,小于0时,由系统确定活动会话
        /// </summary>
        [XmlAttribute()]
        public int SessionId { get; set; }
        /// <summary>
        /// 应用名称
        /// </summary>
        [XmlAttribute()]
        public string ExeName { get; set; }

        /// <summary>
        /// 应用路径
        /// </summary>
        [XmlAttribute()]
        public string Path { get; set; }
        /// <summary>
        /// 心跳文件
        /// </summary>
        [XmlAttribute()]
        public string HeartFile { get; set; }
        /// <summary>
        /// 崩溃间隔(分钟为单位)
        /// </summary>
        [XmlAttribute()]
        public int CrashInterval { get; set; }
        /// <summary>
        /// 是否启用
        /// </summary>
        [XmlAttribute()]
        public bool IsEnabled { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        [XmlAttribute()]
        public string Description { get; set; }
        /// <summary>
        /// 异常窗口标题
        /// </summary>
        [XmlAttribute()]
        public string FatalWindowTitle { get; set; }
        /// <summary>
        /// 关闭按钮文本
        /// </summary>
        [XmlAttribute()]
        public string CloseButtonTitle { get; set; }
    }
}

2、用到了Cjwdev.WindowsApi.dll中的方法,开机时,获取Console的SessionId需要做特殊处理,否则服务进程启用窗体进程,会把窗体进程作为服务启动,从而不会在任务栏出现图标

public partial class AppMonitor : ServiceBase
{
	/// <summary>
	/// 是否运行
	/// </summary>
	private bool IsRunning = false;

	/// <summary>
	/// 监视线程
	/// </summary>
	private System.Threading.Thread m_Monitor;

	/// <summary>
	/// 子窗口回调函数代理
	/// </summary>
	public EnumWindowsProc callBackEnumChildWindows;

	/// <summary>
	/// 枚举主窗口
	/// </summary>
	public EnumWindowsProc callBackEnumWindows;

	public AppMonitor()
	{
		InitializeComponent();

		callBackEnumChildWindows = new EnumWindowsProc(ChildWindowProcess);
		callBackEnumWindows = new EnumWindowsProc(ChildWindowProcess);
	}

	protected override void OnStart(string[] args)
	{
		try
		{
			IsRunning = true;
			m_Monitor = new System.Threading.Thread(new System.Threading.ThreadStart(this.Monitor));
			m_Monitor.Start();
		}
		catch(Exception ex)
		{
			Log.WriteLog("OnStart" + ex.Message);
		}
	}

	protected override void OnStop()
	{
		IsRunning = false;
	}

	protected void Monitor()
	{
		System.Threading.Thread.Sleep(1000);

		//System.Threading.Thread.Sleep(10 * 1000);
		int sessionId = -1;
		while (IsRunning)
		{
			// 加载xml
			string xmlFileName = SystemConst.AppInfoFile;
			try
			{
				string xml = XmlHelper.LoadXmlFromFile(xmlFileName);
				var appInfo = XmlSerializer.Deserialize<AppInfo>(xml);

				if (appInfo != null)
				{
					if (sessionId < 0)
					{
						sessionId = GetActiveSessionId(IntPtr.Zero, appInfo);
					}

					if (sessionId < 0)
					{
						// 暂停1分钟
						System.Threading.Thread.Sleep(1 * 60 * 1000);
						continue;
					}

					foreach (var item in appInfo.Item)
					{
						if (!item.IsEnabled)
							continue;

						// 检查进程
						bool restartApp = false;
						var processList = GetProcess(item.ExeName);
						if (processList == null || processList.Length < 1)
						{
							restartApp = true;
						}
						else
						{
							var process = processList.Where(x => x.MainModule.FileName == item.Path).FirstOrDefault();
							if (process == null)
							{
								restartApp = true;
							}
							else
							{
								//https://www.cnblogs.com/kuangke/p/9524310.html
								//NtQuerySystemInformation
								//if (process.Responding)
								// 如果发生致命错误
								//FindFatalCloseButton(item);

								// 检查进程是否正常
								if (AppIsCrash(item, process))
								{
									// 杀掉进程
									process.Kill();

									restartApp = true;
									// 暂停10秒
									System.Threading.Thread.Sleep(10 * 1000);
								}
							}
						}

						if (restartApp)
						{
							//System.Diagnostics.Process.Start(item.Path);
							// 启动进程
							uint curSessionId = (uint)item.SessionId;
							if (item.SessionId < 0)
							{
								curSessionId = (uint)sessionId;
							}

							StartApp(item.Path, curSessionId);
							Log.WriteLog(string.Format("SessionId: {0}, 启动应用: {1}, 应用路径: {2}", curSessionId, item.ExeName, item.Path));
						}
					}

					// 暂停1分钟
					System.Threading.Thread.Sleep(1 * 60 * 1000);
				}
				else
				{
					Log.WriteLog("Monitor" + "未配置应用参数");
					System.Threading.Thread.Sleep(10 * 60 * 1000);
				}
			}
			catch(Exception ex)
			{
				Log.WriteLog("Monitor" + ex.Message);
				System.Threading.Thread.Sleep(10 * 60 * 1000);
			}
		}
	}

	/// <summary>
	/// 子窗口回调处理函数
	/// </summary>
	/// <param name="hwnd"></param>
	/// <param name="lParam"></param>
	/// <returns></returns>
	public bool ChildWindowProcess(IntPtr hwnd, IntPtr lParam)
	{
		int len;
		StringBuilder title = new StringBuilder(200);
		len = GetWindowText(hwnd, title, 200);

		string winTitle = title.ToString();
		if (!string.IsNullOrEmpty(winTitle))
		{
			string key = Marshal.PtrToStringBSTR(lParam);
			if (key == winTitle)
			{
				SendMessage(hwnd, WM_SETFOCUS, 0, 0);
				SendMessage(hwnd, BM_CLICK, 0, 0);

				Log.WriteLog("Monitor:发生了异常,并自动关闭了程序");
			}
		}

		return true;
	}

	/// <summary>
	/// 单击事件
	/// </summary>
	public const int BM_CLICK = 0x00F5;
	/// <summary>
	/// 获取焦点事件
	/// </summary>
	public const int WM_SETFOCUS = 0x0007;
	/// <summary>
	/// 查找异常关闭按钮
	/// </summary>
	/// <param name="detail"></param>
	private void FindFatalCloseButton(AppInfoDetail detail)
	{
		if (detail == null ||
			string.IsNullOrEmpty(detail.FatalWindowTitle) ||
			string.IsNullOrEmpty(detail.CloseButtonTitle))
			return;

		IntPtr keyPtr = Marshal.StringToBSTR(detail.FatalWindowTitle);
		EnumWindows(callBackEnumWindows, keyPtr);


		//测试警告框
		//主窗口标题
		IntPtr maindHwnd = FindWindowW(null, detail.FatalWindowTitle);
		if (maindHwnd != IntPtr.Zero)
		{
			//按钮控件标题
			IntPtr childHwnd = FindWindowExW(maindHwnd, IntPtr.Zero, null, detail.CloseButtonTitle);
			if (childHwnd != IntPtr.Zero)
			{                    
				SendMessage(childHwnd, WM_SETFOCUS, 0, 0);
				SendMessage(childHwnd, BM_CLICK, 0, 0);

				Log.WriteLog("Monitor:发生了异常,并自动关闭了" + detail.ExeName);
				return;
			}
			else
			{
				keyPtr = Marshal.StringToBSTR(detail.CloseButtonTitle);
				EnumChildWindows(maindHwnd, callBackEnumChildWindows, keyPtr);   
			}
		}
		else
		{
			IntPtr hwd = FindDesktopWindow(detail.FatalWindowTitle);
			if (hwd != IntPtr.Zero)
			{
				keyPtr = Marshal.StringToBSTR(detail.CloseButtonTitle);
				EnumChildWindows(maindHwnd, callBackEnumChildWindows, keyPtr);
			}
		}
	}

	enum GetWindow_Cmd : uint
	{
		GW_HWNDFIRST = 0,
		GW_HWNDLAST = 1,
		GW_HWNDNEXT = 2,
		GW_HWNDPREV = 3,
		GW_OWNER = 4,
		GW_CHILD = 5,
		GW_ENABLEDPOPUP = 6
	}

	[DllImport("user32.dll", EntryPoint = "GetDesktopWindow", CharSet = CharSet.Auto, SetLastError = true)]
	static extern IntPtr GetDesktopWindow();
	[DllImport("user32.dll", EntryPoint = "GetWindow", CharSet = CharSet.Auto, SetLastError = true)]
	static extern IntPtr GetWindow(IntPtr hd, GetWindow_Cmd uCmd);

	/// <summary>
	/// 查找桌面窗口
	/// </summary>
	/// <param name="catpion"></param>
	/// <returns></returns>
	public IntPtr FindDesktopWindow(string catpion)
	{
		IntPtr hd = GetDesktopWindow();

		//得到屏幕上第一个子窗口
		hd = GetWindow(hd, GetWindow_Cmd.GW_CHILD);
		StringBuilder title = new StringBuilder(200);
		//循环得到所有的子窗口
		while (hd != IntPtr.Zero)
		{
			title.Clear();
			int len = GetWindowText(hd, title, 200);

			string winTitle = title.ToString();
			if (!string.IsNullOrEmpty(winTitle))
			{
				if (winTitle.ToLower() == catpion.ToLower())
					return hd;
			}

			hd = GetWindow(hd, GetWindow_Cmd.GW_HWNDNEXT);
		}

		return IntPtr.Zero;
	}

	/// <summary>
	/// 进程是否运行
	/// </summary>
	/// <param name="appName"></param>
	/// <returns></returns>
	protected Process[] GetProcess(string appName)
	{
		System.Diagnostics.Process[] processList = System.Diagnostics.Process.GetProcessesByName(appName);
		return processList;
	}

	/// <summary>
	/// 进程是否崩溃
	/// </summary>
	/// <param name="detail"></param>
	/// <param name="p"></param>
	/// <returns></returns>
	protected bool AppIsCrash(AppInfoDetail detail, Process p)
	{
		// 如果是文件夹,则获取文件夹中文件的最大日期
		if (Directory.Exists(detail.HeartFile))
		{
			DateTime tnow = DateTime.Now.AddDays(-1);
			DateTime LastWriteTime = GetLastFile(detail.HeartFile, tnow);
			var totalMinutes = (DateTime.Now - LastWriteTime).TotalMinutes;
			if (totalMinutes > detail.CrashInterval)
			{
				Log.WriteLog(string.Format("应用:{0},目录{1},最后修改日期{2},离现在{3}分钟", detail.Path, detail.HeartFile, LastWriteTime.ToString("yyyy-MM-dd HH:mm:ss"), totalMinutes));
				return true;
			}
			else
			{
				return false;
			}
		}

		// 如果心跳文件不存在,则检查进程响应状态
		if (File.Exists(detail.HeartFile))
		{
			return !p.Responding;
		}

		// 检查心跳文件
		var lastWriteTime = File.GetLastWriteTime(detail.HeartFile);
		if ((DateTime.Now - lastWriteTime).TotalMinutes > detail.CrashInterval)
		{
			return true;
		}
		else
		{
			return false;
		}
	}

	/// <summary>
	/// 获取最近的文件的时间
	/// </summary>
	/// <param name="dir"></param>
	/// <param name="tnow"></param>
	private DateTime GetLastFile(string dir, DateTime tnow)
	{
		DirectoryInfo di = new DirectoryInfo(dir);

		FileInfo[] fis = di.GetFiles($"*.*");//文件类型
		foreach (FileInfo fi in fis)
		{
			if (fi.LastWriteTime > tnow)
				tnow = fi.LastWriteTime;
		}

		DirectoryInfo[] dis = di.GetDirectories();//目录下的子目录
		foreach (DirectoryInfo item in dis)
		{
			if (item.LastWriteTime > tnow)
				tnow = item.LastWriteTime;

			var tLastTime = GetLastFile(item.FullName, tnow);
			if (tLastTime > tnow)
				tLastTime = tnow;
		}

		return tnow;
	}

	/// <summary>
	/// 启动应用
	/// </summary>
	/// <param name="filePath"></param>
	/// https://blog.csdn.net/weixin_30399821/article/details/97821077
	protected void StartApp(string filePath, uint sessionId)
	{
		try
		{
			string appStartPath = filePath;
			IntPtr userTokenHandle = IntPtr.Zero;
			// 在服务器上,获取的ConsoleSessionId使用后,窗体程序不显示
			// 所以需要在配置文件中配置SessionId,配置为能显示窗体程序的SessionId
			//uint csId = ApiDefinitions.WTSGetActiveConsoleSessionId();       
			ApiDefinitions.WTSQueryUserToken(sessionId, ref userTokenHandle);

			ApiDefinitions.PROCESS_INFORMATION procInfo = new ApiDefinitions.PROCESS_INFORMATION();
			ApiDefinitions.STARTUPINFO startInfo = new ApiDefinitions.STARTUPINFO();
			startInfo.cb = (uint)Marshal.SizeOf(startInfo);

			ApiDefinitions.CreateProcessAsUser(
				userTokenHandle,
				appStartPath,
				"",
				IntPtr.Zero,
				IntPtr.Zero,
				false,
				0,
				IntPtr.Zero,
				null,
				ref startInfo,
				out procInfo);

			//Log.WriteLog("userTokenHandle: " + userTokenHandle.ToString());

			if (userTokenHandle != IntPtr.Zero)
				ApiDefinitions.CloseHandle(userTokenHandle);

			//int pid = (int)procInfo.dwProcessId;
			//var windowHandler = CurrentProcess.GetProcessWindowHandle(pid);
			//Log.WriteLog("currentAquariusProcessId: " + _currentAquariusProcessId.ToString());
		}
		catch (Exception ex)
		{
			Log.WriteLog("StartApp" + ex.Message);
		}
	}

	/// <summary>
	/// 获取活动会话的SessionId
	/// </summary>
	/// <param name="hServer"></param>
	/// <returns></returns>
	public int GetActiveSessionId(IntPtr hServer, AppInfo appInfo)
	{
		IntPtr ppSessionInfo = IntPtr.Zero;
		uint count = 0;

		try
		{
			// 默认为控制台
			string winStationName = "console";
			if (appInfo != null && !string.IsNullOrEmpty(appInfo.WinStationName))
				winStationName = appInfo.WinStationName;

			winStationName = winStationName.ToLower();
			if (WTSEnumerateSessions(hServer, 0, 1, ref ppSessionInfo, ref count))
			{
				Int32 dataSize = Marshal.SizeOf(typeof(WTS_SESSION_INFO));
				Int32 current = (Int32)ppSessionInfo;
				for (int ix = 0; ix < count; ++ix)
				{
					WTS_SESSION_INFO session = new WTS_SESSION_INFO();
					session = (WTS_SESSION_INFO)Marshal.PtrToStructure((IntPtr)current, typeof(WTS_SESSION_INFO));
					current += dataSize;

					Log.WriteLog("SessionId: " + session.SessionId + "; pWinStationName: " + session.pWinStationName);

					// 在阿里云服务器中,因为有RDP-TCP#xx的原因,
					// 即WinStationName名称可能会有变化,所以此处用StartsWith
					// 并且会有一个65536, WinStationName为"RDP-Tcp"的名称
					//if (session.State == WTS_CONNECTSTATE_CLASS.WTSActive &&
					//    session.pWinStationName.ToLower().StartsWith(winStationName))
					if (session.pWinStationName.ToLower().StartsWith(winStationName))
					{
						Log.WriteLog(string.Format("获取到{0}的SessionId:{1} ", session.pWinStationName, session.SessionId));
						return (int)session.SessionId;
					}
				}
			}

			//return ApiDefinitions.WTSGetActiveConsoleSessionId();
		}
		finally
		{
			int error = Marshal.GetLastWin32Error();
			WTSFreeMemory(ppSessionInfo);
		}

		return -1;
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值