C#代码安装Windows服务(控制台应用集成Windows服务)
最近在为一款C/S架构的科研软件开发云计算版,需要用到WCF,考虑到不需要什么界面以及稳定性,无人值守性,准备用Windows Service作为宿主,无奈Windows Service的安装太为繁复,就想如何通过C#代码完成Windows服务的安装及配置,在网上找了些资料,大多都是非常简单的代码,并没有一个完整的示例,可能一些初学者看起来不是很清晰,特别做了这个Demo!
首先建立项目,结构非常简单,一个控制台应用程序,添加一个类库(ServiceHelper.cs 安装、卸载服务辅助类),一个Window服务(WindowsService.cs)

项目的思路就是,启动控制台应用程序,自动识别是应该以服务身份启动服务还是以控制台应用身份启动服务配置
首先来看Windows服务定义

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)
{
// TODO: 在此处添加代码以启动服务。
System.IO.File.AppendAllText(@"D:\Log.txt", " Service Start :" + DateTime.Now.ToString());
} protected override void OnStop()
{
// TODO: 在此处添加代码以执行停止服务所需的关闭操作。
System.IO.File.AppendAllText(@"D:\Log.txt", " Service Stop :" + DateTime.Now.ToString());
}
}
}

为了简单演示,这里我没有做什么工作,只是简单的往D盘写入了一个日志,证明服务正常工作就行了
然后来看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 = ; i < ; i++)
{
service.Refresh();
System.Threading.Thread.Sleep();
if (service.Status == System.ServiceProcess.ServiceControllerStatus.Running)
{
break;
}
if (i == )
{
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);
}
}
}
}

这里有四个函数,分别是用于验证指定服务是否存在,启动服务,获取指定服务状态和最关键的服务安装及卸载(服务配置)
验证服务存在性及获取服务状态没什么好说的,启动服务因为会有一个延时,通过一个循环模拟等待服务启动的这段时间,超时即为启动失败,重点是第四个服务配置函数
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 > )
{
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 > )
{
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
转自:http://www.cnblogs.com/MadisonSky/p/5848446.html
C#代码安装Windows服务(控制台应用集成Windows服务)的更多相关文章
- C#代码安装、卸载、监控Windows服务
C#编写Windows服务之后都不可避免的需要安装,卸载等操作.而传统的方式就是通过DOS界面去编写命令,这样的操作方式无疑会增加软件实施人员的工作量,下面就介绍一种简单.高效.快速方便的方式.1.安 ...
- c#创建windows服务(代码方式安装、启动、停止、卸载服务)
转载于:https://www.cnblogs.com/mq0036/p/7875864.html 一.开发环境 操作系统:Windows 10 X64 开发环境:VS2015 编程语言:C# .NE ...
- 微软 WCF的几种寄宿方式,寄宿IIS、寄宿winform、寄宿控制台、寄宿Windows服务
WCF寄宿方式是一种非常灵活的操作,可以在IIS服务.Windows服务.Winform程序.控制台程序中进行寄宿,从而实现WCF服务的运行,为调用者方便.高效提供服务调用.本文分别对这几种方式进行详 ...
- 禅道ZenTao在windows和Lniux下集成安装环境和一键安装方法整理
一共4种安装方法看官可以根据你自己的实际环境来选择一个都很简单 windows下用禅道官网的一键安装包方法(推荐): 为了简化大家在windows下面的安装,我们在xampp基础上做了禅道的windo ...
- Windows服务一:新建Windows服务、安装、卸载服务
Windows 服务(即,以前的 NT 服务)使您能够创建在它们自己的 Windows 会话中可长时间运行的可执行应用程序.这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面 ...
- .NET Core 代码安装服务启动
最近做了一些.NET Core的程序,有在Windows下运行的 有在CentOS 下运行的,Windows下运行的还好,对Windows下还算比较熟悉了,但CentOS 下 每次都是找笔记支持命令 ...
- windows服务搭建(VS2019创建Windows服务不显示安装组件)
1.创建windows服务应用 2.右键查看代码 3.写个计时器Timer using System.Timers; 如上图,按tab键快速操作 会自动创建一个委托 改为下边的方式,打印日志来记录 ...
- [SDK2.2]Windows Azure Storage (16) 使用WCF服务,将本地图片上传至Azure Storage (上) 客户端代码
<Windows Azure Platform 系列文章目录> 前一章我们完成了服务器端的代码,并且已经发布到了Windows Azure云端. 本章我们将实现客户端的代码,客户端这里我们 ...
- kafka 以windows服务的方式在windows下安装并自启动
准备工作: 下载kafka http://apache.fayea.com/kafka/0.10.0.0/kafka_2.10-0.10.0.0.tgz 解压kafka至D:\bigdata\kafk ...
随机推荐
- 将外部dwg图纸中指定带属性的块插入到当前图纸中
static void InsertBlock() { //获取要插入的块名 TCHAR str[40]; acedGetString(Adesk::kFalse, _T("\n请输入要插入 ...
- linux学习之路(1)
Linux基础命令 命令格式: 命令名称 [参数] [对象] 命令参数的长格式与短格式: 长格式:man -- help 短格式:man - h 常用系统工作命令 echo 输出语句 ...
- 神奇的Scala Macro之旅(三)- 实际应用
在上一篇中,我们示范了使用macro来重写 Log 的 debug/info 方法,并大致的介绍了 macro 的基本语法.基本使用方法.以及macro背后的一些概念, 如AST等.那么,本篇中,我们 ...
- mysql怎样配置ODBC数据源
一个基于ODBC的应用程序对数据库的操作不依赖任何DBMS,不直接与DBMS打交道,所有的数据库操作由对应的DBMS的ODBC驱动程序完成.所以说mysql配置ODBC数据源也很重要. 工具/原料 ...
- 腾讯云存储专家深度解读基于Ceph对象存储的混合云机制
背景 毫无疑问,乘着云计算发展的东风,Ceph已经是当今最火热的软件定义存储开源项目.如下图所示,它在同一底层平台之上可以对外提供三种存储接口,分别是文件存储.对象存储以及块存储,本文主要关注的是对象 ...
- 3D数学 矩阵常用知识点整理
1.矩阵了解 1)矩阵的维度和记法 (先数多少行,再数多少列) 2)矩阵的转置 行变成列,第一行变成第一列...矩阵的转置的转置就是原矩阵 即 3)矩阵和标量的乘法 ...
- 「拥抱开源, 又见 .NET」系列第三次线下活动简报
「拥抱开源, 又见 .NET」 随着 .NET Core的发布和开源,.NET又重新回到人们的视野. 自2016年 .NET Core 1.0 发布以来,其强大的生命力让越来越多技术爱好者对她的未来满 ...
- 从零单排学Redis【铂金一】
前言 只有光头才能变强 好的,今天我们要上铂金段位了,如果还没经历过青铜和白银和黄金阶段的,可以先去蹭蹭经验再回来: 从零单排学Redis[青铜] 从零单排学Redis[白银] 从零单排学Redis[ ...
- 使用docker-compose 一键部署你的分布式调用链跟踪框架skywalking
一旦你的程序docker化之后,你会遇到各种问题,比如原来采用的本地记日志的方式就不再方便了,虽然你可以挂载到宿主机,但你使用 --scale 的话,会导致 记录日志异常,所以最好的方式还是要做日志中 ...
- 你需要知道的这几种 asp.net core 修改默认端口的方式
一般情况下,aspnetcore发布后的默认端口是5000,这个大家都知道,而且默认骨架代码中没有看到任何让你输入的ip地址和端口号,但作为程序员的我们,不希望 被框架所管制,那如何实现默认端口的修改 ...