如何创建一个标准的Windows服务

出处:http://www.cnblogs.com/wuhuacong/archive/2009/02/11/1381428.html

如何创建一个标准的Windows服务

在很多时候,我们需要一个定时器,当间隔某段时间或者在某一个时刻的时候,触发某个业务的处理,这个时候,我们就可能需要引入Windows服务来做这个事情,如某些数据的同步操作、某些工作任务的创建或者侦听某些端口的工作等等。
做过Windows Forms开发的人,对开发Windows服务可能会熟悉一些,其实它本身应该算是一个Windows Forms程序。基本上整个Windows服务的程序分为几个部分:安装操作实现、程序启动、服务操作等。
本例子创建一个Windows服务,服务可以在整点运行,也可以在某段间隔时间运行,通过配置指定相关的参数。
完整的服务代码请下载文件进行学习: http://files.cnblogs.com/wuhuacong/AutoSyncService.rar  

1)安装操作类的实现
首先需要继承System.Configuration.Install.Installer类,并且需要增加ServiceProcessInstaller、ServiceInstaller两个对象来处理,另外您需要重载BeforeUninstall 和 AfterInstall 来实现服务在安装前后的启动和停止操作。
WinserviceInstaller.png

    [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上设置的一个参数, 如下图所示。
WinserviceDebug.png


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

winservice.png
卸载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
主要研究技术:代码生成工具、Visio二次开发、客户关系管理软件、病人资料管理软件、送水管理软件等共享软件开发
专注于Winform开发框架Web开发框架、WCF开发框架、微信门户开发框架的研究及应用。
  转载请注明出处:
撰写人:伍华聪  http://www.iqidi.com 
    

转载于:https://www.cnblogs.com/Mindy-hym/p/4362429.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值