zoukankan      html  css  js  c++  java
  • 从一个简单的例子对win 服务程序进行讲解

    前言先说明一下本文win 服务程序的简单需求,即根据外部配置实现不同方式记录日志的功能。记录日志的方式有三种,分为文本记录、数据库记录和文本与数据库同时记录。

    一、win 服务程序的开发

    1、新建win服务程序

    打开开发神器VS(这里用的是VS2012),单击“新建项目”,在弹出的选项卡上左侧选择“Windows”,然后在右侧选择“Windows服务“模板,确定即可。按照命名需要,本文示例中我把VS默认生成的Service1重命名为LogService。如下图:

    (1)、构造函数

    public LogService()
            {
                InitializeComponent();
                this.ServiceName = "SimpleLogService";
                CanStop = true;
    
                
                CanPauseAndContinue = true;
                CanShutdown = true;
                CanHandleSessionChangeEvent = true;
                 
            }
    

    我们在构造函数里设置了几个常用的属性。ServiceName 为服务的名字,其中CanPauseAndContinue = true;标识该服务可以暂停和继续。我们也可以在设计界面设置相关属性值:

    (2)、方法事件

    默认情况下,在LogService类中VS已经替我们生成了OnStart和OnStop方法。如果我们还设置了属性 CanPauseAndContinue = true, 则我们可能还要重写OnPause和OnContinue方法。在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 DotNet.Common.Util;
    using System.Threading;
    
    
    
    
    namespace SimpleLogService
    {
        public partial class LogService : ServiceBase
        {
            
    
            public LogService()
            {
                InitializeComponent();
                this.ServiceName = "SimpleLogService";
                CanStop = true;            
                CanPauseAndContinue = true;
                CanShutdown = true;
    
                CanHandleSessionChangeEvent = true;
                 
            }
    
            /// <summary>
            /// 启动服务
            /// </summary>
            /// <param name="args"></param>
            protected override void OnStart(string[] args)
            {
                Logger.AppendLog("服务启动", LogBuilder.logDir);
                LogBuilder.Start();
    
            }
    
            /// <summary>
            ///  终止服务
            /// </summary>
            protected override void OnStop()
            {
                try
                {
                    if (LogBuilder.dictWorkThread == null)
                    {
                        return;
                    }
                    foreach (KeyValuePair<string, Thread> item in LogBuilder.dictWorkThread)
                    {
                        if (item.Value == null)
                        {
                            continue;
                        }
                        item.Value.Abort();
                        Logger.AppendLog(string.Format("{0}线程已经终止", item.Value.Name), LogBuilder.logDir);
                    }
    
                }
                finally
                {
                    base.OnStop();
                }
            }
    
    
           
            /// <summary>
            /// 暂停服务
            /// </summary>
            protected override void OnPause()
            {
                try
                {
                    if (LogBuilder.dictWorkThread == null)
                    {
                        return;
                    }
                    foreach (KeyValuePair<string, Thread> item in LogBuilder.dictWorkThread)
                    {
                        if (item.Value == null || item.Value.IsAlive == false)
                        {
                            continue;
                        }
                        if ((item.Value.ThreadState & (System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) > 0)
                        {
                            Logger.AppendLog("线程暂停!" + item.Value.ThreadState, LogBuilder.logDir);
                            continue;
                        }
                        item.Value.Suspend();//线程挂起
                        Logger.AppendLog(string.Format("{0}线程已经暂停工作", item.Value.Name), LogBuilder.logDir);
                    }
                }
                finally
                {
                    base.OnPause();
                }
            }
    
    
            /// <summary>
            /// 继续暂停的服务(恢复)
            /// </summary>
            protected override void OnContinue()
            {
                try
                {
                    if (LogBuilder.dictWorkThread == null)
                    {
                        return;
                    }
                    foreach (KeyValuePair<string, Thread> item in LogBuilder.dictWorkThread)
                    {
                        if (item.Value == null || item.Value.IsAlive == false)
                        {
                            continue;
                        }
                        if ((item.Value.ThreadState & (System.Threading.ThreadState.Suspended | System.Threading.ThreadState.SuspendRequested)) == 0)
                        {
                            continue;
                        }
                        item.Value.Resume();//继续已经挂起的线程
                        Logger.AppendLog(string.Format("{0}线程已经开始继续工作", item.Value.Name), LogBuilder.logDir);
                    }
                }
                finally
                {
                    base.OnContinue();
                }
            }
        
    
        }
    }
    

    需要说明的是,windows服务都是在后台默默无闻地低调工作着,所以对开发人员来讲,通常长时间大批量的后台工作任务,做成windows服务再合适不过。但是如果您的程序实现使用了异步,就会给服务的停止、暂停和恢复等控制带来极大难度,而且有时候甚至会产生意想不到的结果。本文示例中对于停止、暂停和恢复,都是对一个静态线程进行操作。实际开发中这种方式并不保险,因为异步程序中你实在不好控制程序到底执行到哪一步,执行的结果怎么样。我估计微软默认不生成暂停和恢复这两个事件,也是基于控制不易方面的考虑。在实际项目开发中,除非可以明确确定异步程序已经暂时不工作(通过查看特定日志),否则“暂停”和“恢复”这两个按钮通常默认都是不可用的(CanPauseAndContinue = false)。

    2、为服务添加安装程序

    服务的主体实现已经有了,当然还需要服务安装程序逻辑。打开LogService设计界面,右键选择”Add Installer“栏目,这里你可以根据VS提供的可视化的方式给两个Installer进行属性设置。

    注:在VS2012里面通过代码来设置属性好像不起作用(红色部分)

    public ProjectInstaller()
         {
             InitializeComponent();
             ServiceInstaller installer = new ServiceInstaller();
             installer.ServiceName = "SimpleLogService";//服务的名称要和LogService构造函数里的服务名称一致
             installer.DisplayName = "测试日志记录Windows服务";//windows服务显示的名称
             installer.Description = "这是一个简单的测试日志记录Windows服务,在log文件夹下可以看到详细文本日志";
             installer.StartType = ServiceStartMode.Manual; // 自动 手动 或禁用 这里设为手动
     
             ServiceProcessInstaller processInstaller = new ServiceProcessInstaller();
             // 采用本地系统帐户运行服务
             processInstaller.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
             processInstaller.Username = null;
             processInstaller.Password = null;
             this.Installers.AddRange(new System.Configuration.Install.Installer[] { installer, processInstaller });
         }
    

    到现在为止,一个简单的win 服务程序就已开发完成

    二、win 服务的安装及卸载以及可能出现的问题的解决方案


    1、使用微软提供的Installutil工具来安装以及卸载win 服务

    在命令行中输入:

    安装: path/InstallUtil.exe  pathmyAssembly.exe

    卸载: path/InstallUtil.exe  pathmyAssembly.exe -u

    注:path为路径 myAssembly为程序编译之后的可执行文件的名字 例子:

    C:/WINDOWS/Microsoft.NET/Framework/v4.0.30319/InstallUtil.exe E:ProgrammerASP.NETSimpleLogServiceSimpleLogServiceinReleaseSimpleLogService.exe -u

    在下载服务在重新安装服务可能出现的问题

    解决方案:  http://blog.sina.com.cn/s/blog_645e2f0b0101b5c3.html

    2、启动及暂停服务

    开启服务:

    net start ServiceName

    停止服务:

    net stop ServiceName

    在开启服务时可能出现的问题:

    解决方案:http://blog.itpub.net/221062/viewspace-481588/

     到这里,初步的写个win 服务的基本知识以及准备工作已经完成了,最后附上例子的结果图片:

      

     

      

      

  • 相关阅读:
    【Educational Codeforces Round 36 C】 Permute Digits
    【Educational Codeforces Round 36 B】Browser
    【Educational Codeforces Round 36 A】 Garden
    【习题 8-14 UVA
    【习题 8-13 UVA
    【习题 8-12 UVA
    【习题 8-11 UVA
    【习题 8-10 UVA
    关于货仓选址问题的方法及证明(在数轴上找一点使得该点到所有其他点的距离之和最小)
    P2512 [HAOI2008]糖果传递
  • 原文地址:https://www.cnblogs.com/YanYongSong/p/4453544.html
Copyright © 2011-2022 走看看