如何创建一个标准的Windows服务
出处:http://www.cnblogs.com/wuhuacong/archive/2009/02/11/1381428.html
如何创建一个标准的Windows服务
做过Windows Forms开发的人,对开发Windows服务可能会熟悉一些,其实它本身应该算是一个Windows Forms程序。基本上整个Windows服务的程序分为几个部分:安装操作实现、程序启动、服务操作等。
本例子创建一个Windows服务,服务可以在整点运行,也可以在某段间隔时间运行,通过配置指定相关的参数。
完整的服务代码请下载文件进行学习:http://files.cnblogs.com/wuhuacong/AutoSyncService.rar
1)安装操作类的实现
首先需要继承System.Configuration.Install.Installer类,并且需要增加ServiceProcessInstaller、ServiceInstaller两个对象来处理,另外您需要重载BeforeUninstall 和 AfterInstall 来实现服务在安装前后的启动和停止操作。
[RunInstaller(true)]
public class ListenInstaller : Installer
{
private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller;
private System.ServiceProcess.ServiceInstaller serviceInstaller; /// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null; public ListenInstaller()
{
InitializeComponent();
//重新覆盖设计时赋予的服务名称
this.serviceInstaller.DisplayName = Constants.ServiceName;
this.serviceInstaller.ServiceName = Constants.ServiceName;
}
public override void Install(System.Collections.IDictionary stateSaver)
{
base.Install(stateSaver);
} private void serviceInstaller_AfterInstall(object sender, InstallEventArgs e)
{
ServiceController service = new ServiceController(Constants.ServiceName); if (service.Status != ServiceControllerStatus.Running)
{
try
{
service.Start();
}
catch (Exception ex)
{
EventLog loger;
loger = new EventLog();
loger.Log = "Application";
loger.Source = Constants.ServiceName;
loger.WriteEntry(ex.Message + "\n" + ex.StackTrace, EventLogEntryType.Error);
}
}
} private void serviceInstaller_BeforeUninstall(object sender, InstallEventArgs e)
{
ServiceController service = new ServiceController(Constants.ServiceName); if (service.Status != ServiceControllerStatus.Stopped)
{
try
{
service.Stop();
}
catch (Exception ex)
{
EventLog loger;
loger = new EventLog();
loger.Log = "Application";
loger.Source = Constants.ServiceName;
loger.WriteEntry(ex.Message + "\n" + ex.StackTrace, EventLogEntryType.Error);
}
}
}
...............
}
2)程序启动
程序的启动很简单,基本上是自动创建服务程序的时候就生成了,这里列出来解析是为了说明服务调试的操作。
程序的启动是在Main函数里面,添加下面的代码即可
ServiceBase[] ServicesToRun; ServicesToRun = new ServiceBase[] { new SocketService() }; ServiceBase.Run(ServicesToRun);
上面是标准的启动代码,但很多时候,我们需要调试服务,因此会加入一个跳转的开关
#region 调试程序时使用的代码
//使用方法:在该Project的属性页,设置输入参数"-T",即可进入下面这段代码,发布时请去掉参数;
if (args.Length >= 1 && args[0].ToUpper() == "-T")
{
try
{
SocketService service = new SocketService();
service.Execute();
}
catch (Exception ex)
{
throw ex;
}
return;
} #endregion
上面的操作就是为了可以使用普通的调试功能调试Windows服务,其中的"-T"是在开发工具VS的IDE上设置的一个参数, 如下图所示。
3)服务操作
首先需要创建一个集成自System.ServiceProcess.ServiceBase的服务类,如SocketService服务类,在SocketService类的构造函数中,您可能需要初始化一些信息,如创建一个定时器,修改服务器类的名称,读取配置参数等信息,以便初始化服务类的参数。
接着您需要重载服务基类的一些函数:OnStart、OnStop、OnContinue、OnPause、OnShutdown和定时器的触发函数timerReAlarm_Elapsed。完整的类如下
public class SocketService : ServiceBase
{
private static readonly ILog logger = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private AppConfig appConfig = new AppConfig();
private System.Timers.Timer timerReAlarm; private int ServiceCycle = 1;//服务运行间隔,和整点运行相斥(单位分钟)
private int CycleCount = 0;//间隔的服务运行计数(单位分钟) private int ServiceRunAt = 0;//整点运行服务时间,负数为禁用(单位小时)
private bool RunAtOnce = false;//整点运行服务是否已经运行 private bool GBLService = false;//是否启动GBL同步服务
private bool DomainService = false;//是否启动域用户同步 /// <summary>
/// 必需的设计器变量。
/// </summary>
private System.ComponentModel.IContainer components = null;
private System.Diagnostics.EventLog eventLog; public SocketService()
{
InitializeComponent(); eventLog = new EventLog();
eventLog.Log = "Application";
eventLog.Source = Constants.ServiceName; this.ServiceName = Constants.ServiceName; try
{
//系统心跳
int interval = int.Parse(appConfig.AppConfigGet("TimerInterval"));
interval = (interval < 1) ? 1 : interval;//不能太小
timerReAlarm = new System.Timers.Timer(interval * 60000);//分钟
timerReAlarm.Elapsed += new ElapsedEventHandler(timerReAlarm_Elapsed); //服务运行间隔
ServiceCycle = int.Parse(appConfig.AppConfigGet("ServiceCycle"));
ServiceCycle = (ServiceCycle < interval) ? interval : ServiceCycle;//不能小于心跳 //服务整点运行
ServiceRunAt = int.Parse(appConfig.AppConfigGet("ServiceRunAt")); GBLService = Convert.ToBoolean(appConfig.AppConfigGet("GBLService"));
DomainService = Convert.ToBoolean(appConfig.AppConfigGet("DomainService")); logger.Info(Constants.ServiceName + "已初始化完成");
}
catch (Exception ex)
{
logger.Error(Constants.ServiceName + "初始化错误", ex);
}
} /// <summary>
/// 设置具体的操作,以便服务可以执行它的工作。
/// </summary>
protected override void OnStart(string[] args)
{
logger.Info(Constants.ServiceName + "开始启动。"); timerReAlarm.Start();
eventLog.WriteEntry(Constants.ServiceName + "已成功启动。", EventLogEntryType.Information); CreateTask();
} /// <summary>
/// 停止此服务。
/// </summary>
protected override void OnStop()
{
timerReAlarm.Stop();
} /// <summary>
/// 暂停后继续运行
/// </summary>
protected override void OnContinue()
{
timerReAlarm.Start();
base.OnContinue();
} /// <summary>
/// 暂停
/// </summary>
protected override void OnPause()
{
timerReAlarm.Stop();
base.OnPause();
} /// <summary>
/// 关闭计算机
/// </summary>
protected override void OnShutdown()
{
base.OnShutdown();
} private void timerReAlarm_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
CreateTask();
} /// <summary>
/// 使用线程池方式运行程序
/// 优点:快速启动Windows服务, 在后台继续程序操作.
/// </summary>
private void CreateTask()
{
ThreadPool.QueueUserWorkItem(new WaitCallback(ExecuteTask), null);
} /// <summary>
/// 开始执行同步任务
/// </summary>
private void ExecuteTask(object status)
{
try
{
//采用整点运行方式
if(ServiceRunAt > 0)
{
if(DateTime.Now.Hour == ServiceRunAt)
{
if(!RunAtOnce)
{
Execute();
RunAtOnce = true;//标识整点已经运行过了
}
}
else
{
RunAtOnce = false;
} }
else//采用间隔运行方式
{
//不管服务间隔是否心跳的倍数,只要服务间隔时间大于等于间隔时间,就执行一次
if(CycleCount >= ServiceCycle)
{
Execute();
CycleCount = 0;
}
else
{
CycleCount++;
}
}
}
catch (Exception ex)
{
logger.Error("ExecuteTask()函数发生错误", ex);
eventLog.WriteEntry(Constants.ServiceName + "运行时出现异常!\r\n" + ex.Message + "\r\n" + ex.Source + "\r\n" + ex.StackTrace);
}
} public void Execute()
{
//初始化数据库连接
string DatabasePassword = Sys.decode(ConfigurationSettings.AppSettings.Get("DatabasePassword"));
DAO.init(ConfigurationSettings.AppSettings.Get("DatabaseConnect").Replace("{$password}", DatabasePassword), DAO.DATABASE_SQLSERVER); //初始化数据库(SQL Server)访问对象
Lib.adPasswd = Sys.decode(ConfigurationSettings.AppSettings.Get("password"));
if(GBLService)
{
AutomatismXml xml = new AutomatismXml();
xml.AutomatismXmlData(0); logger.Info(Constants.ServiceName + DateTime.Now.ToShortTimeString() + "已成功调用了GBLService一次。");
eventLog.WriteEntry(DateTime.Now.ToShortTimeString() + "已成功调用了GBLService一次。", EventLogEntryType.Information);
}
if(DomainService)
{
string msg = string.Empty;
string path = ConfigurationSettings.AppSettings.Get("path");
string username = ConfigurationSettings.AppSettings.Get("username");
string domain = ConfigurationSettings.AppSettings.Get("domain");
AD.init(path, username, Lib.adPasswd, domain); DomainHelper.accountSync(true, false, true, ref msg, 1);
Log.saveADLog(null, "系统同步域用户:" + msg); logger.Info(Constants.ServiceName + DateTime.Now.ToShortTimeString() + "已成功调用了DomainService一次。");
eventLog.WriteEntry(DateTime.Now.ToShortTimeString() + "已成功调用了DomainService一次。", EventLogEntryType.Information);
}
} ...................
}
4. 使用InstallUtil来安装和卸载服务
安装和卸载Windows服务,需要使用InstallUtil工具类进行操作,该工具是Dotnet框架附带的一个工具,在%SystemRoot%\Microsoft.NET\Framework\*** 对应的目录中。
其中App.config中的内容如下
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<!--心跳间隔,系统设置,单位(分钟)-->
<add key="TimerInterval" value="5" />
<!-- 运行同步服务的间隔时间(单位:分钟) -->
<add key="ServiceCycle" value="60" />
<!--Windows服务在固定时刻(0~23时刻)运行,设置了该参数,同步服务间隔参数无效,负数为禁用-->
<add key="ServiceRunAt" value="-1" />
<!--是否启动GBL信息自动同步服务-->
<add key="GBLService" value="True" />
<!--是否启动域用户信息自动同步服务-->
<add key="DomainService" value="True" />
</appSettings>
</configuration>
安装Windows服务的命令如下:
@ECHO OFF
REM The following directory is for .NET1.1
set DOTNETFX=%SystemRoot%\Microsoft.NET\Framework\v1.1.4322
set PATH=%PATH%;%DOTNETFX%
cd\
cd "%SystemRoot%\..\Program Files\BornShine\用户信息同步服务"
echo 正在安装 用户信息同步服务
echo ---------------------------------------------------
InstallUtil /i AutoSyncService.exe
echo ---------------------------------------------------
echo Done.
exit
卸载Windows服务的命令如下:
@ECHO OFF REM The following directory is for .NET1.1
set DOTNETFX=%SystemRoot%\Microsoft.NET\Framework\v1.1.4322
set PATH=%PATH%;%DOTNETFX% cd\
cd "%SystemRoot%\..\Program Files\BornShine\用户信息同步服务" echo 正在卸载 用户信息同步服务
echo --------------------------------------------------- InstallUtil /U AutoSyncService.exe echo --------------------------------------------------- echo Done.
exit
专注于Winform开发框架、Web开发框架、WCF开发框架、微信门户开发框架的研究及应用。
转载请注明出处:
撰写人:伍华聪 http://www.iqidi.com
如何创建一个标准的Windows服务的更多相关文章
- 创建一个简单的windows服务,每间隔一定时间重复执行批处理文件
创建一个windows服务项目,增加App.config <?xml version="1.0" encoding="utf-8" ?> <c ...
- 使用MicroService4Net 快速创建一个简单的微服务
“微服务架构(Microservice Architecture)”一词在过去几年里广泛的传播,它用于描述一种设计应用程序的特别方式,作为一套独立可部署的服务.目前,这种架构方式还没有准确的定义,但是 ...
- 微服务配置内容《网上copy》=========》如何创建一个高可用的服务注册中心
前言:首先要知道什么是一个高可用的服务注册中心,基于spring boot建成的服务注册中心是一个单节点的服务注册中心,这样一旦发生了故障,那么整个服务就会瘫痪,所以我们需要一个高可用的服务注册中心, ...
- 使用Axis2创建一个简单的WebService服务
使用过Java进行过WebService开发都会听过或者接触过Apache Axis2,Axis2框架是应用最广泛的WebService框架之一了. 这里使用Axis2来开发和部署一个最简单的WebS ...
- NPOI-Excel系列-1000.创建一个标准的Excel文件
using NPOI.HSSF.UserModel; using Microsoft.VisualStudio.TestTools.UnitTesting; using System.IO; name ...
- node创建一个简单的web服务
本文将如何用node创建一个简单的web服务,过程也很简单呢~ 开始之前要先安装node.js 1.创建一个最简单的服务 // server.js const http = require('http ...
- java最简单的知识之创建一个简单的windows窗口,利用Frame类
作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 QQ986945193 微博:http://weibo.com/mcxiaobing 首先给大家看一下 ...
- 如何成功发布一个MSMQ的Windows服务
因为MSMQ的使用需要不断的查看队列是否有新消息,所以一般是结合Windows的服务,当然也可以用一个不关闭的Winform程序,不过前者更好一些,不怕被人误关. 完成MSMQ的WindowsServ ...
- VB6 如何创建一个标准控制台程序
打开 VB6 并新建一个标准EXE程序,把窗口删掉,然后再加入一个模块. 在模块中加入AllocConsole.FreeConsole.SetConsoleTitle.Sleep的API声明: Pub ...
随机推荐
- windows生成dump文件
windows下程序有时突然崩溃了,偶发性的崩溃很难找.于是就需要保存崩溃时的dump信息了. 下面是关于如何生成dmp文件的代码. 头文件 #pragma once #include <win ...
- 20155324 2016-2017-2 《Java程序设计》第6周学习总结
20155324 2016-2017-2 <Java程序设计>第6周学习总结 教材学习内容总结 InputStream与OutputStream 串流设计 1.串流:Java将输入/输出抽 ...
- ubuntu16.04安装nvidia驱动及CUDA+cudnn
网上查了资料,装好了,参照以下 https://blog.csdn.net/zhang970187013/article/details/81012845 https://blog.csdn.net/ ...
- JS/javaScript 获取div内容
jquery: 例如<div id="abc"><a>内容</a></div>$("#abc").html(); ...
- ue4网络同步概念笔记
网络同步主要的概念是 复制. 不是以前的S,C逻辑分离. 是S复制到C通过 Switch Has Authority 将S与C的逻辑在程序内分离. 所属权:每个连接到S的C都有自己的 控制权.从 ...
- Python 获取文件中最长行的长度和最长行
1, 使用文件 #vim /etc/motd "1 hello world" 2 ...... yes 3 no you are a shadiao 4 hahh maye you ...
- t-sql对被除数为0&除数小于被除数结果为0&除法保留2位小数的处理
SELECT round(CAST(12 AS FLOAT)/nullif(13,0),2,1) FROM TB
- 资源中心的ES 服务的COM.IFLYTEK.ERSP.API.RESOURCEAPI 接口注册ZOOKEEPER失败,解决记录
No provider available for the service com.iflytek.ersp.api.ResourceApi from the url zookeeper://zook ...
- Android逆向基础----Dalvik字节码
参考此微博,更多详细内容可以到这里查看 http://blog.csdn.net/dd864140130/article/details/52076515 Dalvik字节码 1.寄存器位32位,64 ...
- POJ 1458 Common Subsequence 最长公共子序列
题目大意:求两个字符串的最长公共子序列 题目思路:dp[i][j] 表示第一个字符串前i位 和 第二个字符串前j位的最长公共子序列 #include<stdio.h> #include&l ...