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 ...
随机推荐
- BZOJ_1132_[POI2008]Tro_计算几何
BZOJ_1132_[POI2008]Tro_计算几何 Description 平面上有N个点. 求出所有以这N个点为顶点的三角形的面积和 N<=3000 Input 第一行给出数字N,N在[3 ...
- BZOJ_3362_[Usaco2004 Feb]Navigation Nightmare 导航噩梦_并查集
BZOJ_3362_[Usaco2004 Feb]Navigation Nightmare 导航噩梦_并查集 Description 农夫约翰有N(2≤N≤40000)个农场,标号1到N,M( ...
- BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心
BZOJ_1826_[JSOI2010]缓存交换 _线段树+贪心 Description 在计算机中,CPU只能和高速缓存Cache直接交换数据.当所需的内存单元不在Cache中时,则需要从主存里把数 ...
- iOS 远程推送通知 详解
1: ios本地通知和远程通知 http://wangjun.easymorse.com/?p=1482 2: 苹果远程通知服务申请激活例图 (外国佬写的.) http://mobiforge.com ...
- RabbitMQ windows本地安装
1: 安装RabbitMQ需要先安装Erlang语言开发包.下载地址 http://www.erlang.org/download.html 配置环境变量 ERLANG_HOME C:\Program ...
- sql server 任务调度与CPU
一. 概述 我们知道在操作系统看来, sql server产品与其它应用程序一样,没有特别对待.但内存,硬盘,cpu又是数据库系统最重要的核心资源,所以在sql server 2005及以后出现了SQ ...
- MySQL数据同步,出现Slave_SQL_Running:no和slave_io_running:no问题的解决方法
一.问题描述: 当我们配置好MySQL主主同步时,是可以实现主主同步,但是重启机器后就发现无法同步了. 二.Slave两个关键进程: mysql replication 中slave机器上有两个关键的 ...
- mariaDB vs mysql
mariaDB vs mysql 今天遇到一个库使用的是mariaDB的数据库版本 Server version: 10.1.20-MariaDB MariaDB Server 理了一下mariaDB ...
- WinForm的DataGirdView判断CheckBox是否被选中
首先我们先设置下DataGirdView的列. 然后启动下编辑,就可以选中与不选中了.在之后通过. #region 便利被选中的行,然后导出 DataTable dtreport = new Data ...
- sql自动生成golang结构体struct实体类
废话不多说直接上地址 使用地址 http://www.linkinstars.com:8090/auto-code 项目github https://github.com/LinkinStars/Au ...