赞
踩
目录
当需要在 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 右键选择查看代码,如下:
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Diagnostics;
- using System.Linq;
- using System.ServiceProcess;
- using System.Text;
- using System.Threading.Tasks;
-
- namespace WindowsService1
- {
- public partial class Service1 : ServiceBase
- {
- public Service1()
- {
- InitializeComponent();
- }
-
- protected override void OnStart(string[] args)
- {
- }
-
- protected override void OnStop()
- {
- }
- }
- }
这里重写了一个 OnStart 和 OnStop 方法,意思是程序启动的时候执行一次,和程序关闭的时候执行一次。
因为目前只是作为演示,就不用过多的功能,就随便输出一些日志,能理解 Windows 服务的用法就好了。
安装插件 log4net
新建一个类 Logger,用来输出日志
- using System;
-
- [assembly: log4net.Config.XmlConfigurator(ConfigFile = "log4net.config", ConfigFileExtension = "config", Watch = true)]
- public class Logger
- {
- private static readonly log4net.ILog loginfo = log4net.LogManager.GetLogger("loginfo");
- private static readonly log4net.ILog logerror = log4net.LogManager.GetLogger("logerror");
-
- public static void WriteInfo(string info)
- {
- Console.WriteLine(info);
- if (loginfo.IsInfoEnabled)
- {
- loginfo.Info(info);
- }
- }
-
- public static void WriteError(string error)
- {
- Console.WriteLine(error);
- if (logerror.IsErrorEnabled)
- {
- logerror.Error(error);
- }
- }
-
- public static void WriteError(string info, Exception ex)
- {
- Console.WriteLine(info);
- if (logerror.IsErrorEnabled)
- {
- logerror.Error(info, ex);
- }
- }
- }
log4net 要想输出日志,还需要另外添加一个配置文件 log4net.config
- <?xml version="1.0"?>
- <configuration>
- <configSections>
- <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
- </configSections>
- <log4net>
- <!-- 错误日志类-->
- <logger name="logerror">
- <level value="ALL" />
- <appender-ref ref="ErrorAppender" />
- </logger>
- <!-- 信息日志类 -->
- <logger name="loginfo">
- <level value="ALL" />
- <appender-ref ref="InfoAppender" />
- </logger>
- <!-- 错误日志附加介质-->
- <appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
- <param name="File" value="Log\\LogError\\" />
- <param name="AppendToFile" value="true" />
- <param name="MaxSizeRollBackups" value="100" />
- <param name="MaxFileSize" value="10240" />
- <param name="StaticLogFileName" value="false" />
- <param name="DatePattern" value="yyyyMMdd".htm"" />
- <param name="RollingStyle" value="Date" />
- <!--布局-->
- <layout type="log4net.Layout.PatternLayout">
- <param name="ConversionPattern" value="<HR COLOR=red>%n异常时间:%d [%t] <BR>%n异常级别:%-5p <BR>%n异 常 类:%c [%x] <BR>%n%m <BR>%n <HR Size=1>" />
- </layout>
- </appender>
- <!-- 信息日志附加介质-->
- <appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
- <param name="File" value="Log\\LogInfo\\" />
- <param name="AppendToFile" value="true" />
- <param name="MaxFileSize" value="10240" />
- <param name="MaxSizeRollBackups" value="100" />
- <param name="StaticLogFileName" value="false" />
- <param name="DatePattern" value="yyyyMMdd".htm"" />
- <param name="RollingStyle" value="Date" />
- <!-- 信息日志布局-->
- <layout type="log4net.Layout.PatternLayout">
- <param name="ConversionPattern" value="<HR COLOR=blue>%n日志时间:%d [%t] <BR>%n日志级别:%-5p <BR>%n日 志 类:%c [%x] <BR>%n%m <BR>%n <HR Size=1>" />
- </layout>
- </appender>
- </log4net>
- </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 代码
- using System;
- using System.Collections;
- using System.Configuration.Install;
- using System.IO;
- using System.ServiceProcess;
- using System.Windows.Forms;
-
-
- namespace ServiceControl
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- }
-
- //服务程序路径
- private string ServiceFilePath = string.Empty;
- //服务程序名
- private string ServiceName = string.Empty;
-
- private void Form1_Load(object sender, EventArgs e)
- {
-
- }
-
-
- /// <summary>
- /// 选择服务程序路径 点击事件
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void Button_SelectPath_Click(object sender, EventArgs e)
- {
- string serviceName = TextBox_ServiceName.Text;
- if (string.IsNullOrEmpty(serviceName))
- {
- Console.WriteLine("请先输入服务程序名");
- return;
- }
- ServiceName = serviceName;
-
- OpenFileDialog openFileDialog = new OpenFileDialog();
- if (openFileDialog.ShowDialog() == DialogResult.OK)
- {
- //文件路径
- string filePath = openFileDialog.FileName;
- TextBox_ServicePath.Text = filePath;
- ServiceFilePath = filePath;
- }
- }
-
- /// <summary>
- /// 启动服务 点击事件
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void Button_StartService_Click(object sender, EventArgs e)
- {
- try
- {
- if (!IsServiceExisted(ServiceName))
- {
- Console.WriteLine("请先安装服务程序:{0}", ServiceName);
- return;
- }
- ServiceStart(ServiceName);
- Console.WriteLine("启动服务 完成");
- }
- catch (Exception ex)
- {
- Console.WriteLine("启动服务程序错误:{0}", ex.Message);
- }
- }
-
- /// <summary>
- /// 停止服务 点击事件
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void Button_StopService_Click(object sender, EventArgs e)
- {
- try
- {
- if (!IsServiceExisted(ServiceName))
- {
- Console.WriteLine("请先安装服务程序:{0}", ServiceName);
- return;
- }
- ServiceStop(ServiceName);
- Console.WriteLine("停止服务 完成");
- }
- catch (Exception ex)
- {
- Console.WriteLine("停止服务程序错误:{0}", ex.Message);
- }
- }
-
- /// <summary>
- /// 安装服务 点击事件
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void Button_InstallerService_Click(object sender, EventArgs e)
- {
- try
- {
- if(IsServiceExisted(ServiceName))
- {
- Console.WriteLine("当前服务程序已经安装,不能重复安装");
- return;
- }
- InstallService(ServiceFilePath);
- Console.WriteLine("安装服务 完成");
- }
- catch (Exception ex)
- {
- Console.WriteLine("安装服务程序错误:{0}", ex.Message);
- }
- }
-
- /// <summary>
- /// 卸载服务 点击事件
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void Button_UnloadService_Click(object sender, EventArgs e)
- {
- try
- {
- if (!IsServiceExisted(ServiceName))
- {
- Console.WriteLine("请先安装服务程序:{0}", ServiceName);
- return;
- }
-
- ServiceStop(ServiceName);
- UninstallService(ServiceFilePath);
- Console.WriteLine("卸载服务 完成");
- }
- catch (Exception ex)
- {
- Console.WriteLine("卸载服务程序错误:{0}", ex.Message);
- }
- }
-
- /// <summary>
- /// 判断服务程序是否存在
- /// </summary>
- /// <param name="serviceName">服务程序的名字</param>
- /// <returns></returns>
- private bool IsServiceExisted(string serviceName)
- {
- if (string.IsNullOrEmpty(serviceName))
- {
- Console.WriteLine("参数 serviceName 不能为空");
- return false;
- }
-
- System.ServiceProcess.ServiceController[] services =
- System.ServiceProcess.ServiceController.GetServices();
- foreach (var item in services)
- {
- if (item.ServiceName.ToLower() == serviceName.ToLower())
- return true;
- }
- return false;
- }
-
- /// <summary>
- /// 安装服务
- /// </summary>
- /// <param name="servicePath">服务程序的路径</param>
- private void InstallService(string servicePath)
- {
- if (string.IsNullOrEmpty(servicePath))
- {
- Console.WriteLine("参数 servicePath 不能为空");
- return;
- }
-
- using (AssemblyInstaller installer = new AssemblyInstaller())
- {
- installer.UseNewContext = true;
- installer.Path = servicePath;
- IDictionary savedState = new Hashtable();
- installer.Install(savedState);
- installer.Commit(savedState);
- }
- }
-
- /// <summary>
- /// 卸载服务
- /// </summary>
- /// <param name="servicePath">服务程序的路径</param>
- private void UninstallService(string servicePath)
- {
- if (string.IsNullOrEmpty(servicePath))
- {
- Console.WriteLine("参数 servicePath 不能为空");
- return;
- }
-
- using (AssemblyInstaller installer = new AssemblyInstaller())
- {
- installer.UseNewContext = true;
- installer.Path = servicePath;
- installer.Uninstall(null);
- }
- }
-
- /// <summary>
- /// 启动服务
- /// </summary>
- /// <param name="serviceName">服务程序的名字</param>
- private void ServiceStart(string serviceName)
- {
- if (string.IsNullOrEmpty(serviceName))
- {
- Console.WriteLine("参数 serviceName 不能为空");
- return;
- }
-
- using (System.ServiceProcess.ServiceController control =
- new System.ServiceProcess.ServiceController(serviceName))
- {
- if (control.Status == System.ServiceProcess.ServiceControllerStatus.Stopped)
- control.Start();
- }
- }
-
- /// <summary>
- /// 停止服务
- /// </summary>
- /// <param name="serviceName">服务程序的名字</param>
- private void ServiceStop(string serviceName)
- {
- if (string.IsNullOrEmpty(serviceName))
- {
- Console.WriteLine("参数 serviceName 不能为空");
- return;
- }
-
- using (System.ServiceProcess.ServiceController control =
- new System.ServiceProcess.ServiceController(serviceName))
- {
- if (control.Status == System.ServiceProcess.ServiceControllerStatus.Running)
- control.Stop();
- }
- }
- }
- }
下面操作会对 VS 配置有影响,不用管理员的身份打开 VS 每次都会弹框,请慎重操作。
由于启动服务程序需要管理员权限,现在我们添加权限,右键点击项目,添加--> 新建项,选择 应用程序清单文件:
将原有的代码注释掉,下图中的代码复制出来:
点击保存,并将输出类型改为控制台输出
第一次运行,会提示你是否提升权限,点击 使用其他凭据重新启动
但是后面项目每次打开都提示这个弹框,请慎重操作。
运行项目,选择服务程序的路径
点击 安装服务 按钮,控制台输出了一大堆文字,最后可以看到 “安装服务完成” 的打印。
接下来,我们打开服务程序,找到列表中 T 字开头的服务程序,可以看到我们刚刚安装的 TestService1,在说明这里写着 “测试服务程序”,就是上面操作中手动添加进去的。
打开属性看看:
找到服务程序的安装目录,会多两个文件出来
点击按钮 启动服务 按钮
控制台输出:
在服务程序中,可以看到 “正在运行” 的文字,那么启动服务就是成功了。
如果你之前已经打开了服务程序,点击一下刷新按钮就好了
在服务程序目录,会多一个文件夹,打开这个文件夹
在 LogInfo 文件夹中找到当天的日期的日志文件
在浏览器中打开,就能看到代码中输出的日志
点击 停止服务 按钮
控制台输出:
在服务程序列表中,“正在运行” 的状态已经变成了一片空白,那么就是成功了。
我们打开日志文件:
点击 卸载服务 按钮
控制台同样输出了一大堆文字,这里执行会稍微慢点,界面会卡死,执行完成后,在最后一行可以看到 “卸载服务完成” 的打印,那么就是成功了,这时界面的卡死也恢复了正常。(单线程的缘故)
打开服务程序,可以看到,我们安装的 TestService1 服务程序已经没有了
那么这就是服务程序大致的操作了。
源码:点击跳转
如果这个帖子对你有所帮助,欢迎 关注 + 点赞 + 留言
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。