您的位置:首页 > 新闻 > 资讯 > C# 进程守护之Windows服务

C# 进程守护之Windows服务

2024/10/9 9:15:31 来源:https://blog.csdn.net/qq_27410185/article/details/142127905  浏览:    关键词:C# 进程守护之Windows服务
所谓进程守护,就是想让我们的主程序能够长期、稳定的运行下去。单独一个主程序的话,很难做到这个事情。所以我们需要第二个程序来守护它,让它能够不被轻易停掉或者停掉后能自动再启动起来。

守护进程实现的方式有很多种,简单说下:

  1. 做两个程序,在主程序启动的时候去启动守护程序,守护程序需要是一个无交互、无页面的后台程序,守护程序用来不断检测主程序是否存在,如果不存在,则去启动它。同时也可以让则两个程序相互守护;
  2. 用Windows计划任务来守护程序,在主程序启动的时候,创建一个Windows计划任务来判断主程序的状态以及重启它,这种方式直接委托到系统中,其实也很稳妥。
  3. 用Windows服务来定时检测主程序。Windows服务一般用于后台处理,不需要界面操作,可以永远驻留系统,可以设定为自动启动和手动启动。所以这个我认为更能胜任这个工作。

下面介绍下Windows服务的创建以及基本使用

  1. 用Visual Studio创建项目的时候选择windows服务

  1. 创建后系统会默认创建一个Service1.cs文件,双击打开这个文件,然后在该页面上右键->添加安装程序
  2. 接下来会看到项目下多了个ProjectInstaller.cs文件,打开这个文件会看到里面有两个控件;serviceProcessInstaller1serviceInstaller1
  3. serviceProcessInstaller1是服务流程安装程序,这里我们只需要设置下服务的账户类型;即:serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
  4. serviceInstaller1就是我们的服务安装程序了,在这里可以设置服务的名称、描述以及启动方式等信息,同时还可以对一些安装事件进行处理
  5. 我们再回到Service1文件看下代码文件,会发现里面有OnStart和OnStop两个方法,分别表示服务启动和停止时需要执行的事件,这里可以写我们需要处理的逻辑代码

介绍完上面的基本情况之后,我们再随便创建一个控制台程序,这是主程序,我们要做的就是守护这个程序,这里随便写个代码好了。

为了方便复用,还要新增一个公共类库,封装一些操作类等。所以最终程序的结构如下:

因为最后生成之后,我们的服务与主程序会是两个不同的程序,但是我们又需要通信,所以这里增加了配置文件,即Config.xml,这个文件记录了一些服务的基本信息;同时为了更加人性化,也可以说是为了扩展点小知识吧。使用了注册表来存储程序的路径(因为我们不知道客户会把程序安装到哪里,所以在重启的时候找路径就比较困难。在启动主程序的时候将路径写入到注册表不失为一个很好的办法);

写了这么多,下面放下代码;

实现功能:

- 守护进程,当主程序被停掉的时候重新启动

开发环境:

开发工具: Visual Studio 2013

.NET Framework版本:4.5

实现代码:

  1. 配置文件
<?xml version="1.0" encoding="utf-8" ?>
<Service><!--服务名称--><ServiceName>ProtectApp</ServiceName><!--服务描述--><ServiceDesc>守护进程服务</ServiceDesc><!--主程序进程名称--><ProcessName>WindowsService.App</ProcessName><!--注册表路径--><RegeditPath>SOFTWARE\\ProtectApp</RegeditPath><!--注册表键名--><RegeditKey>Path</RegeditKey>
</Service>
  1. WindowsService的代码
 //Service1public partial class Service1 : ServiceBase{System.Timers.Timer timer1 = new System.Timers.Timer(5000);private string appPath, processName;public Service1(){InitializeComponent();timer1.Elapsed += timer1_Tick;}protected override void OnStart(string[] args){XElement xele = XElement.Load(Path.GetDirectoryName(typeof(WindowsService.Program).Assembly.Location) + "\\Config.xml");processName = xele.Element("ProcessName").Value;string subKey = xele.Element("RegeditPath").Value;string keyName = xele.Element("RegeditKey").Value;appPath = new RegeditUtil(RegistryKeyRoot.HKEY_LOCAL_MACHINE).GetValue(subKey, keyName).ToString();timer1.Start();}protected override void OnStop(){}private void timer1_Tick(object sender, EventArgs e){Process[] process = Process.GetProcessesByName(processName);if (process.Length == 0){if (System.IO.File.Exists(appPath)){ServiceUtil.RunApp(appPath);}}}}//ProjectInstallerpublic partial class ProjectInstaller : System.Configuration.Install.Installer{public ProjectInstaller(){InitializeComponent();serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;XElement xele = XElement.Load(Path.GetDirectoryName(typeof(WindowsService.Program).Assembly.Location) + "\\Config.xml");serviceInstaller1.ServiceName = xele.Element("ServiceName").Value;serviceInstaller1.Description = xele.Element("ServiceDesc").Value;}}
  1. 注册表操作工具类
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;namespace WindowsService.Common
{
public class RegeditUtil
{private readonly RegistryKey registryKey;public RegeditUtil(RegistryKeyRoot registryKeyRoot){switch (registryKeyRoot){case RegistryKeyRoot.HKEY_CLASSES_ROOT:registryKey = Registry.ClassesRoot;break;case RegistryKeyRoot.HKEY_CURRENT_USER:registryKey = Registry.CurrentUser;break;case RegistryKeyRoot.HKEY_LOCAL_MACHINE:registryKey = Registry.LocalMachine;break;case RegistryKeyRoot.HKEY_USERS:registryKey = Registry.Users;break;case RegistryKeyRoot.HKEY_CURRENT_CONFIG:registryKey = Registry.CurrentConfig;break;default:registryKey = Registry.LocalMachine;break;}}public object GetValue(string SubKey, string keyName){RegistryKey key = registryKey.OpenSubKey(SubKey);if (key != null){return key.GetValue(keyName);}else{return null;}}public Dictionary<string, object> GetAllValue(string SubKey){Dictionary<string, object> dic = new Dictionary<string, object>();RegistryKey key = registryKey.OpenSubKey(SubKey);if (key != null){string[] arr = key.GetValueNames();for (int i = 0; i < arr.Length; i++){dic.Add(arr[i], key.GetValue(arr[i]));}}return dic;}public void SetValue(string SubKey, Dictionary<string, object> keyValue){RegistryKey key = registryKey.OpenSubKey(SubKey, true) ?? registryKey.CreateSubKey(SubKey, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryOptions.Volatile);foreach (var d in keyValue){key.SetValue(d.Key, d.Value);}}public void SetValue(string SubKey,string keyName,object value){Dictionary<string, object> dic = new Dictionary<string, object>();dic.Add(keyName, value);SetValue(SubKey, dic);}public void RemoveKey(string SubKey, string keyName){RegistryKey key = registryKey.OpenSubKey(SubKey);if (key != null){key.DeleteSubKeyTree(keyName);}}
}public enum RegistryKeyRoot{HKEY_CLASSES_ROOT,HKEY_CURRENT_USER,HKEY_LOCAL_MACHINE,HKEY_USERS,HKEY_CURRENT_CONFIG}
}
  1. Windows服务工具类
using Cjwdev.WindowsApi;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Threading.Tasks;namespace WindowsService.Common
{
public class ServiceUtil
{private readonly string ServiceName;public ServiceUtil(string serviceName){ServiceName = serviceName;}/// <summary>/// 安装服务/// </summary>/// <param name="ServiceAppPath">安装路径</param>public void Install(string ServiceAppPath){string basePath = AppDomain.CurrentDomain.BaseDirectory;List<string> cmds = new List<string>();cmds.Add(string.Format("{0}InstallUtil.exe {1}", basePath, ServiceAppPath));cmds.Add(string.Format("sc stop {0}", ServiceName));cmds.Add(string.Format("sc config {0} type= interact type= own", ServiceName));cmds.Add(string.Format("sc start {0}", ServiceName));RunCmd(cmds);}/// <summary>/// 卸载服务/// </summary>/// <param name="ServiceAppPath">安装路径</param>public void UnInstall(string ServiceAppPath){string basePath = AppDomain.CurrentDomain.BaseDirectory;List<string> cmds = new List<string>();cmds.Add(string.Format("{0}InstallUtil.exe /u {1}", basePath, ServiceAppPath));RunCmd(cmds);}/// <summary>/// 开启服务/// </summary>/// <param name="Server"></param>public void Start(){ServiceController control = new ServiceController(ServiceName);if (control.Status == ServiceControllerStatus.Stopped){control.Start();}}/// <summary>/// 关闭服务/// </summary>/// <param name="Server"></param>public void Stop(){ServiceController control = new ServiceController(ServiceName);if (control.Status == ServiceControllerStatus.Running){control.Stop();}}/// <summary>/// 删除服务/// </summary>public void Remove(){string basePath = AppDomain.CurrentDomain.BaseDirectory;List<string> cmds = new List<string>();cmds.Add(string.Format("sc delete {0}", ServiceName));RunCmd(cmds);}/// <summary>/// 查看服务是否存在/// </summary>/// <param name="NameService"></param>/// <returns></returns>public bool Exists(){ServiceController[] services = ServiceController.GetServices();foreach (ServiceController s in services){if (s.ServiceName.ToUpper() == ServiceName.ToUpper()){return true;}}return false;}/// <summary>/// 设置允许服务与桌面交互 ,修改了注册表,要重启系统才能生效/// </summary>public void SetInteraction(){RegeditUtil regeditUtil = new RegeditUtil(RegistryKeyRoot.HKEY_LOCAL_MACHINE);string subKey = @"SYSTEM/CurrentControlSet/Services/" + ServiceName;string keyName = "Type";int value = (int)regeditUtil.GetValue(subKey, keyName);regeditUtil.SetValue(subKey, keyName, value | 256);}/// <summary>/// 禁用任务管理器/// </summary>/// <param name="arg">0.启用;1.禁用 </param>public static void ManageTaskManager(bool isEnable){RegeditUtil regeditUtil = new RegeditUtil(RegistryKeyRoot.HKEY_CURRENT_USER);string subKey = @"Software\Microsoft\Windows\CurrentVersion\Policies\System";string keyName = "DisableTaskmgr";int value = isEnable ? 0 : 1;regeditUtil.SetValue(subKey, keyName, value);}/// <summary>///winservice打开可执行程序///win7以上系统不允许服务与桌面交互,所以此处借用Cjwdev.WindowsApi.dll来实现/// </summary>/// <param name="AppPath"></param>/// <returns></returns>public static int RunApp(string AppPath){IntPtr userTokenHandle = IntPtr.Zero;ApiDefinitions.WTSQueryUserToken(ApiDefinitions.WTSGetActiveConsoleSessionId(), 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,AppPath,"",IntPtr.Zero,IntPtr.Zero,false,0,IntPtr.Zero,null,ref startInfo,out procInfo);if (userTokenHandle != IntPtr.Zero)ApiDefinitions.CloseHandle(userTokenHandle);return (int)procInfo.dwProcessId;}public List<string> RunCmd(List<string> cmds){List<string> list = new List<string>();try{Process p = new Process();p.StartInfo.UseShellExecute = false;p.StartInfo.CreateNoWindow = true;p.StartInfo.FileName = "cmd.exe";p.StartInfo.RedirectStandardError = true;p.StartInfo.RedirectStandardInput = true;p.StartInfo.RedirectStandardOutput = true;p.Start();foreach (string cmd in cmds){p.StandardInput.WriteLine(cmd);}p.StandardInput.WriteLine("exit");p.StandardInput.AutoFlush = true;StreamReader readerout = p.StandardOutput;while (!readerout.EndOfStream){list.Add(readerout.ReadLine());}p.WaitForExit();p.Close();return list;}catch (Exception ex){list.Add(ex.Message);return list;}}}
}
  1. 控制台(主程序代码)
using System.Xml.Linq;namespace WindowsService.App
{
class Program
{static void Main(string[] args){Console.Title="服务测试";RegeditUtil regeditUtil = new RegeditUtil(RegistryKeyRoot.HKEY_LOCAL_MACHINE);XElement xele = XElement.Load(Path.GetDirectoryName(typeof(WindowsService.App.Program).Assembly.Location)+"\\Config.xml");string serviceName = xele.Element("ServiceName").Value;string subKey = xele.Element("RegeditPath").Value;string keyName = xele.Element("RegeditKey").Value; string value = Path.GetFullPath(typeof(WindowsService.App.Program).Assembly.Location);if (Convert.ToString(regeditUtil.GetValue(subKey, keyName)) != value){regeditUtil.SetValue(subKey, keyName, value);Console.WriteLine("写入注册表成功");}ServiceUtil serviceUtil = new ServiceUtil(serviceName);if (!serviceUtil.Exists()){string ServicePath = Path.GetFullPath(typeof(WindowsService.Service1).Assembly.Location);serviceUtil.Install(ServicePath);Console.WriteLine("服务安装成功");}while (true){Console.WriteLine(DateTime.Now);Thread.Sleep(3000);}}
}
}

以下几点需要注意:

  1. 程序需要以管理员身份运行
  2. .复制Cjwdev.WindowsApi.dll到输出目录
  3. 服务的安装需要用到InstallUtil.exe,也需要复制到输出目录

实现效果:

效果这里就不放了,可以自行测试下,服务不停,程序不止。。。。

由简入繁,拿来即用

更多精彩,请持续关注公众号:

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com