当前位置:   article > 正文

C# Windows 服务程序的开发_c# windows服务

c# windows服务

目录

一、概述

二、新建服务程序

三、安装前的准备

四、启动、停止,安装、卸载

五、测试

1.安装服务

2.启动服务

3.停止服务

4.卸载服务

结束


一、概述

当需要在 Windows 操作系统以后台服务的形式运行某个应用程序时,可以使用 Windows 服务。Windows 服务是一种特殊类型的应用程序,它可以在操作系统启动时自动启动,并在后台持续运行,而无需用户交互。

Windows 服务通常用于执行一些重要的系统任务,例如定期备份数据、监控系统状态、处理消息队列等。它们在后台默默地运行,不会显示用户界面,但可以通过服务管理器进行配置和监视。

编写一个 Windows 服务程序可以使用 C# 编程语言和 .NET 框架。通过使用 .NET 提供的相关类和方法,可以轻松地创建、安装、启动和停止 Windows 服务。 一个典型的 Windows 服务程序包括以下几个关键部分:

1. 服务类:这是一个继承自 System.ServiceProcess.ServiceBase 类的自定义类。在这个类中,需要重写 OnStart 和 OnStop 方法,分别用于处理服务的启动和停止逻辑。在 OnStart 方法中,可以执行服务的初始化操作,并开始执行后台任务。在 OnStop 方法中,可以执行服务的清理操作,并停止后台任务。

2. 服务主程序:这是一个包含 Main 方法的类,用于启动和停止服务。在 Main 方法中,需要创建一个 ServiceHost 对象,并将服务类作为参数传递给它。然后调用 Start 方法启动服务,并通过调用 Stop 方法停止服务。

3. 安装程序:为了将服务安装到 Windows 系统中,需要编写一个安装程序。安装程序将服务的可执行文件注册到系统服务管理器中,并提供安装、卸载和配置服务的功能。 通过编写一个 Windows 服务程序,可以实现在后台运行的应用程序,以便自动执行某些任务或提供某种功能。这种方式可以确保应用程序在系统启动后始终可用,并且无需用户干预。 请注意,编写和部署 Windows 服务需要一些系统管理权限和操作。确保在进行这些操作之前,对系统有足够的了解,并按照最佳实践进行操作。

二、新建服务程序

在新建项目时,选择 Windows 服务,目前只有 .NET Framework 的服务程序,没有 .Net6 的

或者直接搜索“服务”二字

这里就直接用默认的名字吧

创建完成后的界面

选中 Service1.cs 右键选择查看代码,如下:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Diagnostics;
  6. using System.Linq;
  7. using System.ServiceProcess;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. namespace WindowsService1
  11. {
  12. public partial class Service1 : ServiceBase
  13. {
  14. public Service1()
  15. {
  16. InitializeComponent();
  17. }
  18. protected override void OnStart(string[] args)
  19. {
  20. }
  21. protected override void OnStop()
  22. {
  23. }
  24. }
  25. }

这里重写了一个 OnStart 和 OnStop 方法,意思是程序启动的时候执行一次,和程序关闭的时候执行一次。

因为目前只是作为演示,就不用过多的功能,就随便输出一些日志,能理解 Windows 服务的用法就好了。

安装插件 log4net

新建一个类 Logger,用来输出日志

  1. using System;
  2. [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]
  3. public class Logger
  4. {
  5. private static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("loginfo");
  6. private static readonly log4net.ILog logerror = log4net.LogManager.GetLogger("logerror");
  7. public static void WriteInfo(string info)
  8. {
  9. Console.WriteLine(info);
  10. if (loginfo.IsInfoEnabled)
  11. {
  12. loginfo.Info(info);
  13. }
  14. }
  15. public static void WriteError(string error)
  16. {
  17. Console.WriteLine(error);
  18. if (logerror.IsErrorEnabled)
  19. {
  20. logerror.Error(error);
  21. }
  22. }
  23. public static void WriteError(string info, Exception ex)
  24. {
  25. Console.WriteLine(info);
  26. if (logerror.IsErrorEnabled)
  27. {
  28. logerror.Error(info, ex);
  29. }
  30. }
  31. }

log4net 要想输出日志,还需要另外添加一个配置文件 log4net.config

  1. <?xml version="1.0"?>
  2. <configuration>
  3. <configSections>
  4. <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
  5. </configSections>
  6. <log4net>
  7. <!-- 错误日志类-->
  8. <logger name="logerror">
  9. <level value="ALL" />
  10. <appender-ref ref="ErrorAppender" />
  11. </logger>
  12. <!-- 信息日志类 -->
  13. <logger name="loginfo">
  14. <level value="ALL" />
  15. <appender-ref ref="InfoAppender" />
  16. </logger>
  17. <!-- 错误日志附加介质-->
  18. <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
  19. <param name="File" value="Log\\LogError\\" />
  20. <param name="AppendToFile" value="true" />
  21. <param name="MaxSizeRollBackups" value="100" />
  22. <param name="MaxFileSize" value="10240" />
  23. <param name="StaticLogFileName" value="false" />
  24. <param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
  25. <param name="RollingStyle" value="Date" />
  26. <!--布局-->
  27. <layout type="log4net.Layout.PatternLayout">
  28. <param name="ConversionPattern" value="&lt;HR COLOR=red&gt;%n异常时间:%d [%t] &lt;BR&gt;%n异常级别:%-5p &lt;BR&gt;%n异 常 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;" />
  29. </layout>
  30. </appender>
  31. <!-- 信息日志附加介质-->
  32. <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
  33. <param name="File" value="Log\\LogInfo\\" />
  34. <param name="AppendToFile" value="true" />
  35. <param name="MaxFileSize" value="10240" />
  36. <param name="MaxSizeRollBackups" value="100" />
  37. <param name="StaticLogFileName" value="false" />
  38. <param name="DatePattern" value="yyyyMMdd&quot;.htm&quot;" />
  39. <param name="RollingStyle" value="Date" />
  40. <!-- 信息日志布局-->
  41. <layout type="log4net.Layout.PatternLayout">
  42. <param name="ConversionPattern" value="&lt;HR COLOR=blue&gt;%n日志时间:%d [%t] &lt;BR&gt;%n日志级别:%-5p &lt;BR&gt;%n日 志 类:%c [%x] &lt;BR&gt;%n%m &lt;BR&gt;%n &lt;HR Size=1&gt;" />
  43. </layout>
  44. </appender>
  45. </log4net>
  46. </configuration>

将 log4net.config 文件设置成:如果较新则复制

服务程序并不能直接启动,安装服务程序需要固定的流程,接下来是安装服务程序的相关内容。

三、安装前的准备

双击 Service1.cs 文件

就打开了 Service1 的设计界面,接着点击 添加安装界面

这时候会自动生成一个 ProjectInstaller 的文件

点击 serviceInstaller1 查看属性

Windows 服务程序主要用到的有几个属性:

Description                服务的说明

ServiceName             服务的名字

StartType                   服务的启动类型

这几个属性可以参考下图:

这里我就随便设置下:

再点击 serviceProcessInstaller1,查看属性

将 Account 设置为 LocalService

以上操作完成后,将项目保存,生成程序。

Windows 服务程序一般用控制台进行安装和卸载的,但控制台用起来不是特别方便,这里我就用 Winform 写一个对应的程序好了。

四、启动、停止,安装、卸载

新一个 Winform 项目,名字叫 ServiceControl, 主要用来操作服务程序的启动、停止,安装、卸载。

创建项目完成后,需要添加一个 dll,名字叫 System.ServiceProcess,一般默认情况下在创建项目时是不会主动添加这个 dll 的,在引用管理器中选择程序集,然后搜索,找到之后添加到项目中就好了。

Winform 界面如下:

Form1 代码

  1. using System;
  2. using System.Collections;
  3. using System.Configuration.Install;
  4. using System.IO;
  5. using System.ServiceProcess;
  6. using System.Windows.Forms;
  7. namespace ServiceControl
  8. {
  9. public partial class Form1 : Form
  10. {
  11. public Form1()
  12. {
  13. InitializeComponent();
  14. }
  15. //服务程序路径
  16. private string ServiceFilePath = string.Empty;
  17. //服务程序名
  18. private string ServiceName = string.Empty;
  19. private void Form1_Load(object sender, EventArgs e)
  20. {
  21. }
  22. /// <summary>
  23. /// 选择服务程序路径 点击事件
  24. /// </summary>
  25. /// <param name="sender"></param>
  26. /// <param name="e"></param>
  27. private void Button_SelectPath_Click(object sender, EventArgs e)
  28. {
  29. string serviceName = TextBox_ServiceName.Text;
  30. if (string.IsNullOrEmpty(serviceName))
  31. {
  32. Console.WriteLine("请先输入服务程序名");
  33. return;
  34. }
  35. ServiceName = serviceName;
  36. OpenFileDialog openFileDialog = new OpenFileDialog();
  37. if (openFileDialog.ShowDialog() == DialogResult.OK)
  38. {
  39. //文件路径
  40. string filePath = openFileDialog.FileName;
  41. TextBox_ServicePath.Text = filePath;
  42. ServiceFilePath = filePath;
  43. }
  44. }
  45. /// <summary>
  46. /// 启动服务 点击事件
  47. /// </summary>
  48. /// <param name="sender"></param>
  49. /// <param name="e"></param>
  50. private void Button_StartService_Click(object sender, EventArgs e)
  51. {
  52. try
  53. {
  54. if (!IsServiceExisted(ServiceName))
  55. {
  56. Console.WriteLine("请先安装服务程序:{0}", ServiceName);
  57. return;
  58. }
  59. ServiceStart(ServiceName);
  60. Console.WriteLine("启动服务 完成");
  61. }
  62. catch (Exception ex)
  63. {
  64. Console.WriteLine("启动服务程序错误:{0}", ex.Message);
  65. }
  66. }
  67. /// <summary>
  68. /// 停止服务 点击事件
  69. /// </summary>
  70. /// <param name="sender"></param>
  71. /// <param name="e"></param>
  72. private void Button_StopService_Click(object sender, EventArgs e)
  73. {
  74. try
  75. {
  76. if (!IsServiceExisted(ServiceName))
  77. {
  78. Console.WriteLine("请先安装服务程序:{0}", ServiceName);
  79. return;
  80. }
  81. ServiceStop(ServiceName);
  82. Console.WriteLine("停止服务 完成");
  83. }
  84. catch (Exception ex)
  85. {
  86. Console.WriteLine("停止服务程序错误:{0}", ex.Message);
  87. }
  88. }
  89. /// <summary>
  90. /// 安装服务 点击事件
  91. /// </summary>
  92. /// <param name="sender"></param>
  93. /// <param name="e"></param>
  94. private void Button_InstallerService_Click(object sender, EventArgs e)
  95. {
  96. try
  97. {
  98. if(IsServiceExisted(ServiceName))
  99. {
  100. Console.WriteLine("当前服务程序已经安装,不能重复安装");
  101. return;
  102. }
  103. InstallService(ServiceFilePath);
  104. Console.WriteLine("安装服务 完成");
  105. }
  106. catch (Exception ex)
  107. {
  108. Console.WriteLine("安装服务程序错误:{0}", ex.Message);
  109. }
  110. }
  111. /// <summary>
  112. /// 卸载服务 点击事件
  113. /// </summary>
  114. /// <param name="sender"></param>
  115. /// <param name="e"></param>
  116. private void Button_UnloadService_Click(object sender, EventArgs e)
  117. {
  118. try
  119. {
  120. if (!IsServiceExisted(ServiceName))
  121. {
  122. Console.WriteLine("请先安装服务程序:{0}", ServiceName);
  123. return;
  124. }
  125. ServiceStop(ServiceName);
  126. UninstallService(ServiceFilePath);
  127. Console.WriteLine("卸载服务 完成");
  128. }
  129. catch (Exception ex)
  130. {
  131. Console.WriteLine("卸载服务程序错误:{0}", ex.Message);
  132. }
  133. }
  134. /// <summary>
  135. /// 判断服务程序是否存在
  136. /// </summary>
  137. /// <param name="serviceName">服务程序的名字</param>
  138. /// <returns></returns>
  139. private bool IsServiceExisted(string serviceName)
  140. {
  141. if (string.IsNullOrEmpty(serviceName))
  142. {
  143. Console.WriteLine("参数 serviceName 不能为空");
  144. return false;
  145. }
  146. System.ServiceProcess.ServiceController[] services =
  147. System.ServiceProcess.ServiceController.GetServices();
  148. foreach (var item in services)
  149. {
  150. if (item.ServiceName.ToLower() == serviceName.ToLower())
  151. return true;
  152. }
  153. return false;
  154. }
  155. /// <summary>
  156. /// 安装服务
  157. /// </summary>
  158. /// <param name="servicePath">服务程序的路径</param>
  159. private void InstallService(string servicePath)
  160. {
  161. if (string.IsNullOrEmpty(servicePath))
  162. {
  163. Console.WriteLine("参数 servicePath 不能为空");
  164. return;
  165. }
  166. using (AssemblyInstaller installer = new AssemblyInstaller())
  167. {
  168. installer.UseNewContext = true;
  169. installer.Path = servicePath;
  170. IDictionary savedState = new Hashtable();
  171. installer.Install(savedState);
  172. installer.Commit(savedState);
  173. }
  174. }
  175. /// <summary>
  176. /// 卸载服务
  177. /// </summary>
  178. /// <param name="servicePath">服务程序的路径</param>
  179. private void UninstallService(string servicePath)
  180. {
  181. if (string.IsNullOrEmpty(servicePath))
  182. {
  183. Console.WriteLine("参数 servicePath 不能为空");
  184. return;
  185. }
  186. using (AssemblyInstaller installer = new AssemblyInstaller())
  187. {
  188. installer.UseNewContext = true;
  189. installer.Path = servicePath;
  190. installer.Uninstall(null);
  191. }
  192. }
  193. /// <summary>
  194. /// 启动服务
  195. /// </summary>
  196. /// <param name="serviceName">服务程序的名字</param>
  197. private void ServiceStart(string serviceName)
  198. {
  199. if (string.IsNullOrEmpty(serviceName))
  200. {
  201. Console.WriteLine("参数 serviceName 不能为空");
  202. return;
  203. }
  204. using (System.ServiceProcess.ServiceController control =
  205. new System.ServiceProcess.ServiceController(serviceName))
  206. {
  207. if (control.Status == System.ServiceProcess.ServiceControllerStatus.Stopped)
  208. control.Start();
  209. }
  210. }
  211. /// <summary>
  212. /// 停止服务
  213. /// </summary>
  214. /// <param name="serviceName">服务程序的名字</param>
  215. private void ServiceStop(string serviceName)
  216. {
  217. if (string.IsNullOrEmpty(serviceName))
  218. {
  219. Console.WriteLine("参数 serviceName 不能为空");
  220. return;
  221. }
  222. using (System.ServiceProcess.ServiceController control =
  223. new System.ServiceProcess.ServiceController(serviceName))
  224. {
  225. if (control.Status == System.ServiceProcess.ServiceControllerStatus.Running)
  226. control.Stop();
  227. }
  228. }
  229. }
  230. }

下面操作会对 VS 配置有影响,不用管理员的身份打开 VS 每次都会弹框,请慎重操作。

由于启动服务程序需要管理员权限,现在我们添加权限,右键点击项目,添加--> 新建项,选择 应用程序清单文件:

将原有的代码注释掉,下图中的代码复制出来:

点击保存,并将输出类型改为控制台输出

第一次运行,会提示你是否提升权限,点击 使用其他凭据重新启动

但是后面项目每次打开都提示这个弹框,请慎重操作

五、测试

1.安装服务

运行项目,选择服务程序的路径

点击 安装服务 按钮,控制台输出了一大堆文字,最后可以看到 “安装服务完成” 的打印。

接下来,我们打开服务程序,找到列表中 T 字开头的服务程序,可以看到我们刚刚安装的  TestService1,在说明这里写着 “测试服务程序”,就是上面操作中手动添加进去的。

打开属性看看:

找到服务程序的安装目录,会多两个文件出来

2.启动服务

 点击按钮 启动服务 按钮

控制台输出:

在服务程序中,可以看到 “正在运行” 的文字,那么启动服务就是成功了。

如果你之前已经打开了服务程序,点击一下刷新按钮就好了

在服务程序目录,会多一个文件夹,打开这个文件夹

在 LogInfo 文件夹中找到当天的日期的日志文件

在浏览器中打开,就能看到代码中输出的日志

3.停止服务

点击 停止服务 按钮

控制台输出:

在服务程序列表中,“正在运行” 的状态已经变成了一片空白,那么就是成功了。

我们打开日志文件:

4.卸载服务

点击 卸载服务 按钮

控制台同样输出了一大堆文字,这里执行会稍微慢点,界面会卡死,执行完成后,在最后一行可以看到 “卸载服务完成” 的打印,那么就是成功了,这时界面的卡死也恢复了正常。(单线程的缘故)

打开服务程序,可以看到,我们安装的  TestService1 服务程序已经没有了

那么这就是服务程序大致的操作了。

源码:点击跳转

结束

如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言

end

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/981520
推荐阅读
相关标签
  

闽ICP备14008679号