最近在为一款C/S架构的科研软件开发云计算版,须要用到WCF,考虑到不须要什么界面以及稳定性,无人值守性,准备用Windows Service做为宿主,无奈Windows Service的安装太为繁复,就想如何经过C#代码完成Windows服务的安装及配置,在网上找了些资料,大多都是很是简单的代码,并无一个完整的示例,可能一些初学者看起来不是很清晰,特别作了这个Demo!html
首先创建项目,结构很是简单,一个控制台应用程序,添加一个类库(ServiceHelper.cs 安装、卸载服务辅助类),一个Window服务(WindowsService.cs)数据库
项目的思路就是,启动控制台应用程序,自动识别是应该以服务身份启动服务仍是以控制台应用身份启动服务配置架构
首先来看Windows服务定义ide
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 ConsoleWithWindowsService { partial class WindowsService : ServiceBase { public WindowsService() { InitializeComponent(); } protected override void OnStart(string[] args) { System.IO.File.AppendAllText(@"D:\Log.txt", " Service Start :" + DateTime.Now.ToString());
new Task(() =>
{函数
// TODO: 在此处添加代码 这里你本身的代码 使用Task 就能够解决原来做者那个启动服务后须要循环60秒判断任务是否开始等一系列问题
}).Start();云计算
} protected override void OnStop() { // TODO: 在此处添加代码以执行中止服务所需的关闭操做。 System.IO.File.AppendAllText(@"D:\Log.txt", " Service Stop :" + DateTime.Now.ToString()); } } }
为了简单演示,这里我没有作什么工做,只是简单的往D盘写入了一个日志,证实服务正常工做就好了spa
而后来看Windows服务的辅助类命令行
using System; using System.Collections; using System.Collections.Generic; using System.Configuration.Install; using System.Linq; using System.Reflection; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace ConsoleWithWindowsService { public class ServiceHelper { /// <summary> /// 服务是否存在 /// </summary> /// <param name="serviceName"></param> /// <returns></returns> public static bool IsServiceExisted(string serviceName) { ServiceController[] services = ServiceController.GetServices(); foreach (ServiceController s in services) { if (s.ServiceName == serviceName) { return true; } } return false; } /// <summary> /// 启动服务 /// </summary> /// <param name="serviceName"></param> public static void StartService(string serviceName) { if (IsServiceExisted(serviceName)) { System.ServiceProcess.ServiceController service = new System.ServiceProcess.ServiceController(serviceName); if (service.Status != System.ServiceProcess.ServiceControllerStatus.Running && service.Status != System.ServiceProcess.ServiceControllerStatus.StartPending) { service.Start(); for (int i = 0; i < 60; i++) { service.Refresh(); System.Threading.Thread.Sleep(1000); if (service.Status == System.ServiceProcess.ServiceControllerStatus.Running) { break; } if (i == 59) { throw new Exception("Start Service Error:" + serviceName); } } } } } /// <summary> /// 获取服务状态 /// </summary> /// <param name="serviceName"></param> /// <returns></returns> public static ServiceControllerStatus GetServiceStatus(string serviceName) { System.ServiceProcess.ServiceController service = new System.ServiceProcess.ServiceController(serviceName); return service.Status; } /// <summary> /// 配置服务 /// </summary> /// <param name="serviceName"></param> /// <param name="install"></param> public static void ConfigService(string serviceName, bool install) { TransactedInstaller ti = new TransactedInstaller(); ti.Installers.Add(new ServiceProcessInstaller { Account = ServiceAccount.LocalSystem }); ti.Installers.Add(new ServiceInstaller { DisplayName = serviceName, ServiceName = serviceName, Description = "MicroID微检系统数据后台服务", ServicesDependedOn = new string[] { "MSSQLSERVER" },//前置服务 StartType = ServiceStartMode.Automatic//运行方式 }); ti.Context = new InstallContext(); ti.Context.Parameters["assemblypath"] = "\"" + Assembly.GetEntryAssembly().Location + "\" /service"; if (install) { ti.Install(new Hashtable()); } else { ti.Uninstall(null); } } } }
这里有四个函数,分别是用于验证指定服务是否存在,启动服务,获取指定服务状态和最关键的服务安装及卸载(服务配置)日志
验证服务存在性及获取服务状态没什么好说的,启动服务由于会有一个延时,经过一个循环模拟等待服务启动的这段时间,超时即为启动失败,重点是第四个服务配置函数code
public static void ConfigService(string serviceName, bool install)
这里咱们须要传入你但愿命名的服务名称,经过一个bool值判断是安装仍是卸载服务,项目中我统一都命名为MyService,接下来看下面这段代码
ti.Installers.Add(new ServiceInstaller { DisplayName = serviceName, ServiceName = serviceName, Description = "MicroID微检系统数据后台服务", ServicesDependedOn = new string[] { "MSSQLSERVER" },//前置服务 StartType = ServiceStartMode.Automatic//运行方式 });
在这段代码中,为服务设置了友好名(DisplayName即为任务管理器、服务管理器中看到的服务名),服务名,说明,前置服务,以及运行方式
这里的前置服务是告诉Windows,启动个人时候,记得要先等待SQLServer启动,我须要用到它,若是你的服务须要访问SQLServer数据库,那可千万不要忘了这里
最后是控制台应用程序的入口
using System; using System.Collections.Generic; using System.Linq; using System.ServiceProcess; using System.Text; using System.Threading.Tasks; namespace ConsoleWithWindowsService { class Program { static void Main(string[] args) { //带参启动运行服务 if (args.Length > 0) { try { ServiceBase[] serviceToRun = new ServiceBase[] { new WindowsService() }; ServiceBase.Run(serviceToRun); } catch (Exception ex) { System.IO.File.AppendAllText(@"D:\Log.txt", "\nService Start Error:" + DateTime.Now.ToString()+"\n"+ex.Message); } } //不带参启动配置程序 else { StartLable: Console.WriteLine("请选择你要执行的操做——1:自动部署服务,2:安装服务,3:卸载服务,4:验证服务状态,5:退出"); Console.WriteLine("————————————————————"); ConsoleKey key = Console.ReadKey().Key; if (key == ConsoleKey.NumPad1 || key == ConsoleKey.D1) { if (ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", false); } if (!ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", true); } ServiceHelper.StartService("MyService"); goto StartLable; } else if (key == ConsoleKey.NumPad2 || key == ConsoleKey.D2) { if (!ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", true); } else { Console.WriteLine("\n服务已存在......"); } goto StartLable; } else if (key == ConsoleKey.NumPad3 || key == ConsoleKey.D3) { if (ServiceHelper.IsServiceExisted("MyService")) { ServiceHelper.ConfigService("MyService", false); } else { Console.WriteLine("\n服务不存在......"); } goto StartLable; } else if (key == ConsoleKey.NumPad4 || key == ConsoleKey.D4) { if (!ServiceHelper.IsServiceExisted("MyService")) { Console.WriteLine("\n服务不存在......"); } else { Console.WriteLine("\n服务状态:" + ServiceHelper.GetServiceStatus("MyService").ToString()); } goto StartLable; } else if (key == ConsoleKey.NumPad5 || key == ConsoleKey.D5) { } else { Console.WriteLine("\n请输入一个有效键!"); Console.WriteLine("————————————————————"); goto StartLable; } } } } }
有了这三部分,就算完成啦,当咱们生成项目后,启动应用程序,这时会默认启动控制台应用程序,由于arg[] 参数为空,有人可能有疑问,那服务又如何启动呢,注意Windows服务辅助类中的这句代码
ti.Context.Parameters["assemblypath"] = "\"" + Assembly.GetEntryAssembly().Location + "\" /service";
咱们在为服务注册的时候,在后面加了"/service"这个参数,也就是说,当咱们直接启动可执行文件时,这个参数为空,程序会启动控制台应用程序,而咱们注册的服务会携带这个参数,你会在后面看到效果,每次服务启动时由于都带了这个参数,程序会自动执行下面的代码来直接启动服务,由此作到了两种程序的动态选择
///带参启动运行服务 if (args.Length > 0) { try { ServiceBase[] serviceToRun = new ServiceBase[] { new WindowsService() }; ServiceBase.Run(serviceToRun); } catch (Exception ex) { System.IO.File.AppendAllText(@"D:\Log.txt", "Service Start Error:" + DateTime.Now.ToString()+"\n"+ex.Message); } }
还有最重要的一点:Win7+系统,因为权限问题,须要为执行程序设置以管理员方式运行,经过VS直接Debug是没法执行成功的,这也是不少网上的老旧资料忽略的一点
如下是运行效果演示
这里我首先获取了一下服务状态,显示服务并不存在,而后经过自动部署(其实就是判断服务状态,不存在则安装,存在则先卸载再安装,最后启动服务),完成后,再次获取服务状态,显示Running!!!
而后咱们进入服务管理器看下
没错,就是它了,跟咱们上面设置的友好名,以及说明一致,而后咱们点开这个服务的属性
你是否是已经看到玄机了,可执行文件路径后面带上了参数,这就是动态选择启动控制台程序仍是启动服务的关键,同时依存关系里面有了SQLServer,接下来咱们作最后一步验证,打开D盘日志文件
至此,证实整套的服务安装,启动均已完成,是否是很方便,之后不再用经过命令行去安装,卸载了,每次均可以直接启动这个控制台应用程序,就能够完成对服务的配置!!!
原创文章,转载请注明出处
最后附上源码
开发环境:Visual studio 2015 / .Net Framework 4.5.2