zoukankan      html  css  js  c++  java
  • 如何创建一个标准的Windows服务

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

    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
  • 相关阅读:
    响应式css样式
    组件 computed 与 vuex 中 getters 的使用,及 mapGetters 的使用,对象上追加属性,合并对象
    nginx 错误集锦
    动态的添加路由
    NProgress的使用 及 路由 token 定向的使用
    token的解码及 判断值不为空的方法
    nginx 的使用
    IT公司100题-tencent-打印所有高度为2的路径
    测试
    Objective-C 与 C++ 的异同
  • 原文地址:https://www.cnblogs.com/jordan2009/p/2141386.html
Copyright © 2011-2022 走看看