• C# Windows服务开发入门


        一、概念名称

        Windows服务(即以前的 NT 服务),使您能够创建在它们自己的Windows会话中可长时间运行的可执行应用程序。这些服务可以在计算机启动时自动启动,可以暂停和重新启动而且不显示任何用户界面。这种服务非常适合在服务器上使用,或任何时候,为了不影响在同一台计算机上工作的其他用户,需要长时间运行功能时使用。

        二、创建Windows服务

        2.1、创建项目

        新建->项目->Windows 桌面->Windows 服务。

        项目右键属性->应用程序->输出类型,可以看出它是属于"Windows 应用程序"。

        2.2、添加安装程序

        打开Service1.cs->空白处右键->添加安装程序。

        2.3、设置安装信息

        打开ProjectInstaller.cs。

        2.3.1、serviceInstaller1

        点击serviceInstaller1,在属性中设置服务信息,此示例是创建一个"HelloWorld"的服务。

    说明:

        Description:服务描述,直接显示到Windows服务列表中的描述。

        DisplayName:服务显示名称,直接显示到Windows服务列表中的名称。

        ServiceName:服务名称,启动或停止服务时的标识。

        StartType:启动类型,如自动、手动等。

        2.3.2、serviceProcessInstaller1

        点击serviceProcessInstaller1,在属性中设置运行服务的账户类型。

        2.4、生成项目

        考虑到后面涉及到的Debugger调试方法,此处选择Release模式进行生成。

        三、安装与卸载服务

        3.1、InstallUtil.exe

        在VS安装目录下将InstallUtil.exe拷贝到项目的Release文件夹下,InstallUtil.exe在VS2017的路径为:C:WindowsMicrosoft.NETFrameworkv4.0.30319。

        3.2、安装服务

        在Release文件夹的地址栏中输入"cmd"调出命令提示符窗体:

        安装服务命令:

    InstallUtil.exe LinkTo.Test.WindowsService.exe

        启动服务命令:

    net start HelloWorld

        当然,一般我们使用批处理的方式来安装与卸载服务。

        在Release文件夹下面,创建一个"安装服务.bat"的批处理文件:

    @echo off
    echo===================================================
    echo      LinkTo.Test.WindowsService 正在安装服务
    echo===================================================
    
    @echo off
    InstallUtil.exe LinkTo.Test.WindowsService.exe
    
    @echo off
    echo===================================================
    echo      LinkTo.Test.WindowsService 正在启动服务
    echo===================================================
    
    @echo off
    net start HelloWorld
    
    pause

        在运行中输入"services.msc"进入服务,即可看到新建的HelloWorld服务:

        3.3、卸载服务

        在Release文件夹下面,创建一个"卸载服务.bat"的批处理文件:

    @echo off
    echo===================================================
    echo      LinkTo.Test.WindowsService 正在停止服务
    echo===================================================
    
    @echo off
    net stop HelloWorld
    
    @echo off
    echo===================================================
    echo      LinkTo.Test.WindowsService 正在卸载服务
    echo===================================================
    
    @echo off
    InstallUtil.exe /u LinkTo.Test.WindowsService.exe
    
    pause

        四、服务定时器

        一般来说,服务都会设置每隔多长时间执行一次任务,这里使用System.Threading.Timer来做个简单的日志记录,将日志写入到ReleaseLog文件夹下。

        public partial class Service1 : ServiceBase
        {
            private static Timer timerAsync = null;
            private int dueTimeInterval = 1000 * 5; //单位:毫秒
            private int periodInterval = 1000 * 5;  //单位:毫秒
    
            public Service1()
            {
                InitializeComponent();
                //callback:一个 TimerCallback 委托,表示要执行的方法。
                //state:一个包含回调方法要使用的信息的对象,或者为空引用。
                //dueTime:调用 callback 之前延迟的时间量(以毫秒为单位)。指定 Timeout.Infinite 以防止计时器开始计时,指定零(0)以立即启动计时器。
                //period:调用 callback 的时间间隔(以毫秒为单位)。指定 Timeout.Infinite 可以禁用定期终止。
                timerAsync = new Timer(AutoAsyncCallback, null, Timeout.Infinite, Timeout.Infinite);
            }
    
            /// <summary>
            /// 服务启动
            /// </summary>
            /// <param name="args"></param>
            protected override void OnStart(string[] args)
            {
                base.OnStart(args);
                timerAsync.Change(dueTimeInterval, periodInterval);
                WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 服务启动" + "
    ");
                WriteLog(Environment.NewLine);
            }
    
            /// <summary>
            /// 服务停止
            /// </summary>
            protected override void OnStop()
            {
                base.OnStop();
                if (timerAsync != null)
                {
                    timerAsync.Change(Timeout.Infinite, Timeout.Infinite);
                    timerAsync.Dispose();
                    timerAsync = null;
                }
                WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 服务停止" + "
    ");
                WriteLog(Environment.NewLine);
            }
    
            /// <summary>
            /// 服务暂停
            /// </summary>
            protected override void OnPause()
            {
                base.OnPause();
                WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 服务暂停" + "
    ");
                WriteLog(Environment.NewLine);
            }
    
            /// <summary>
            /// 计算机关闭
            /// </summary>
            protected override void OnShutdown()
            {
                base.OnShutdown();
                WriteLog(DateTime.Now.ToString("HH:mm:ss") + " 计算机关闭" + "
    ");
                WriteLog(Environment.NewLine);
            }
    
            /// <summary>
            /// 回调函数
            /// </summary>
            /// <param name="state"></param>
            private void AutoAsyncCallback(object state)
            {
                try
                {
                    timerAsync.Change(Timeout.Infinite, Timeout.Infinite);
    #if DEBUG
                    if (!Debugger.IsAttached)
                        Debugger.Launch();      //当进程运行到这里的时候会自动停下来并弹出提示框
                    Debugger.Break();           //这个方法和在VS中加入红色的断点是一模一样的
    #endif
                    WriteLog(DateTime.Now.ToString("HH:mm:ss") + " AutoAsyncCallback执行开始,线程ID = " + Thread.CurrentThread.ManagedThreadId + "
    ");
                    Thread.Sleep(1000 * 10);    //模拟耗时较长的计算任务,且耗时大于定时的间隔时间。
                }
                catch (Exception ex)
                {
                    WriteLog(DateTime.Now.ToString("HH:mm:ss") + " AutoAsyncCallback执行异常:" + "
    " + ex.Message);
                }
                finally
                {
                    timerAsync.Change(dueTimeInterval, periodInterval);
                    WriteLog(DateTime.Now.ToString("HH:mm:ss") + " AutoAsyncCallback执行结束" + "
    ");
                    WriteLog(Environment.NewLine);
                }
            }
    
            /// <summary>
            /// 日志记录
            /// </summary>
            /// <param name="logInfo">日志信息</param>
            void WriteLog(string logInfo)
            {
                try
                {
                    string logDirectory = AppDomain.CurrentDomain.BaseDirectory + "\Log";
                    if (!Directory.Exists(logDirectory))
                    {
                        Directory.CreateDirectory(logDirectory);
                    }
                    string filePath = logDirectory + "\" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
                    File.AppendAllText(filePath, logInfo);
                }
                catch
                { }
            }
        }
    Service1.cs

        五、调试服务

        由于Windows服务程序不能直接执行,所以不能直接打断点进行调试。调试服务的常用方式有以下两种:

        5.1、附加到进程

        服务启动后,点击调试->附加到进程->选择LinkTo.Test.WindowsService->附加。

        5.2、Debugger

    #if DEBUG
                    if (!Debugger.IsAttached)
                        Debugger.Launch();      //当进程运行到这里的时候会自动停下来并弹出提示框
                    Debugger.Break();           //这个方法和在VS中加入红色的断点是一模一样的
    #endif

        使用Debugger代码进行调试,在项目生成的时候,需使用Release模式,否则一直会有附加提示,可在配置管理器中修改Release模式。 

  • 相关阅读:
    [QML] Connections元素介绍
    Common Lisp语言快速入门
    DataGrid模板列取值问题
    DataGrid 中使用 复选框(CheckBox) 删除纪录
    SQL SELECT INTO
    SQL中Case的使用方法(上篇)
    SQL中Case的使用方法(下篇)
    C# ArrayList的用法
    关于 <customErrors> 标记的“mode”属性设置为“Off”的问题的解决方案
    SQL SERVER 中identity
  • 原文地址:https://www.cnblogs.com/atomy/p/12881403.html
Copyright © 2020-2023  润新知