可调试Windows服务框架

参考:

Build A Windows Service Framework

新建ServiceFramework类库,项目中需引用:

using System.Configuration.Install;
using System.ServiceProcess;

三个类BasicServiceStarter、BasicServiceInstaller、BasicService的代码:

class BasicService<T> : ServiceBase where T : IDisposable, new()
    {
        private IDisposable _service;

        protected override void OnStart(string[] args)
        {
            try
            {
                _service = new T();
            }
            catch
            {
                ExitCode = 1064;
                throw;
            }
        }

        protected override void OnStop()
        {
            _service.Dispose();
        }
    }

static class BasicServiceInstaller
    {
        public static void Install(string serviceName)
        {
            CreateInstaller(serviceName).Install(new Hashtable());
        }

        public static void Uninstall(string serviceName)
        {
            CreateInstaller(serviceName).Uninstall(null);
        }

        private static Installer CreateInstaller(string serviceName)
        {
            var installer = new TransactedInstaller();
            installer.Installers.Add(new ServiceInstaller
            {
                ServiceName = serviceName,
                DisplayName = serviceName,
                StartType = ServiceStartMode.Manual
            });
            installer.Installers.Add(new ServiceProcessInstaller
            {
                Account = ServiceAccount.LocalSystem
            });
            var installContext = new InstallContext(
                serviceName + ".install.log", null);
            installContext.Parameters["assemblypath"] =
                Assembly.GetEntryAssembly().Location;
            installer.Context = installContext;
            return installer;
        }
    }

public static class BasicServiceStarter
    {
        public static void Run<T>(string serviceName) where T : IDisposable, new()
        {
            AppDomain.CurrentDomain.UnhandledException += (s, e) =>
            {
                if (EventLog.SourceExists(serviceName))
                {
                    EventLog.WriteEntry(serviceName,
                        "Fatal Exception : " + Environment.NewLine +
                        e.ExceptionObject, EventLogEntryType.Error);
                }
            };

            if (Environment.UserInteractive)
            {
                var cmd =
                    (Environment.GetCommandLineArgs().Skip(1).FirstOrDefault() ?? "")
                    .ToLower();
                switch (cmd)
                {
                    case "i":
                    case "install":
                        Console.WriteLine("Installing {0}", serviceName);
                        BasicServiceInstaller.Install(serviceName);
                        break;
                    case "u":
                    case "uninstall":
                        Console.WriteLine("Uninstalling {0}", serviceName);
                        BasicServiceInstaller.Uninstall(serviceName);
                        break;
                    default:
                        using (var service = new T())
                        {
                            Console.WriteLine(
                                "Running {0}, press any key to stop", serviceName);
                            Console.ReadKey();
                        }
                        break;
                }
            }
            else
            {
                ServiceBase.Run(new BasicService<T> { ServiceName = serviceName });
            }
        }
    }

新建控制台应用程序MyWindowsService,引用ServiceFramework

新建服务应用类,在默认构造函数中执行服务的代码:

class MyService:IDisposable
    {
        public MyService()
        {
            Console.WriteLine("服务启动");
            //do something
        }

        public void Dispose()
        {
            Console.WriteLine("服务停止");
        }
    }

在Main中调用服务:

class Program
    {
        private const string Name = "我的服务";
        static void Main(string[] args)
        {
            BasicServiceStarter.Run<MyService>(Name);
        }
    }

服务的调试及安装与上一篇

使用Topshelf部署Windows服务

相同,这里可以在快捷方式中直接加“i"和”u"即可。

依然可以使用log4net来记录日志:

引用log4net:

install-package log4net

加入log4net.config配置文件,主要此文件一定要始终复制到输出目录

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
  </configSections>
  <log4net>
    <!--定义输出到文件中-->
    <appender name="RollingLogFileAppender" type="log4net.Appender.RollingFileAppender">
      <!--定义文件存放位置-->
      <file value="log\\"/>
      <!--是否追加到文件,默认为true,通常无需设置-->
      <appendToFile value="true"/>
      <!--多线程时采用最小锁定-->
      <lockingModel type="log4net.Appender.FileAppender+MinimalLock"/>
      <!--变换的形式为日志大小-->
      <!--这种情况下MaxSizeRollBackups和maximumFileSize的节点设置才有意义-->
      <!--<rollingStyle value="Size"/>-->
      <!--每天记录的日志文件个数,与maximumFileSize配合使用-->
      <!--<MaxSizeRollBackups value="10"/>-->
      <!--每个日志文件的最大大小-->
      <!--可用的单位:KB|MB|GB-->
      <!--不要使用小数,否则会一直写入当前日志-->
      <!--<maximumFileSize value="2MB"/>-->

      <!--变换的形式为日期,这种情况下每天只有一个日志-->
      <!--此时MaxSizeRollBackups和maximumFileSize的节点设置没有意义-->
      <rollingStyle value="Date"/>

      <!--每分钟写一个文件-->
      <!--<datePattern value="yyyyMMdd-HHmm" />-->
      <!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置-->

      <datePattern value="yyyyMMdd\\HH‘.txt‘"/>
      <staticLogFileName value="false"/>
      <param name="MaxSizeRollBackups" value="100"/>
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%newline %n记录时间:%date %n描述:%message"/>
        <!--<conversionPattern value="%newline %n记录时间:%date %n线程ID:[%thread] %n日志级别:  %-5level %n出错类:%logger property: [%property{NDC}] - %n错误描述:%message%newline %n"/>-->
      </layout>
    </appender>

    <!--定义输出到MySql中-->
    <!--注意 Mysql.data 引用属性中复制本地一定要选True-->
    <appender name="AdoNetAppender_MySql" type="log4net.Appender.AdoNetAppender">
      <bufferSize value="100"/>
      <param name="ConnectionType" value="MySql.Data.MySqlClient.MySqlConnection, MySql.Data"/>
      <param name="ConnectionString" value="server=localhost;database=test;uid=root;pwd=maocaiming;"/>
      <commandText value="INSERT INTO mylog111 (log_datetime,log_thread,log_level,log_logger,log_message,Exception) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)"/>
      <parameter>
        <parameterName value="@log_date"/>
        <dbType value="DateTime"/>
        <layout type="log4net.Layout.RawTimeStampLayout"/>
      </parameter>
      <parameter>
        <parameterName value="@thread"/>
        <dbType value="String"/>
        <size value="255"/>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%thread"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@log_level"/>
        <dbType value="String"/>
        <size value="50"/>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%level"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@logger"/>
        <dbType value="String"/>
        <size value="255"/>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%logger"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@message"/>
        <dbType value="String"/>
        <size value="4000"/>
        <layout type="log4net.Layout.PatternLayout">
          <conversionPattern value="%message"/>
        </layout>
      </parameter>
      <parameter>
        <parameterName value="@exception"/>
        <dbType value="String"/>
        <size value="2000"/>
        <layout type="log4net.Layout.ExceptionLayout"/>
      </parameter>
    </appender>
    <root>
      <!--控制级别,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
      <!--比如定义级别为INFO,则INFO级别向下的级别,比如DEBUG日志将不会被记录-->
      <!--如果没有定义LEVEL的值,则缺省为DEBUG-->
      <level value="ALL"/>
      <!--文件形式记录日志-->
      <appender-ref ref="RollingLogFileAppender"/>
      <!--<appender-ref ref="AdoNetAppender_MySql"/>-->
    </root>
  </log4net>
</configuration>

更新后的Main:

    class Program
    {
        private const string Name = "我的服务";
        static void Main(string[] args)
        {
            FileInfo fi = new FileInfo(AppDomain.CurrentDomain.BaseDirectory + "log4net.config");
            XmlConfigurator.ConfigureAndWatch(fi);
            BasicServiceStarter.Run<MyService>(Name);
        }
    }

更新后的MyService:

class MyService:IDisposable
    {
        readonly Timer _timer;
        readonly ILog _log = LogManager.GetLogger(typeof(MyService));
        public MyService()
        {
            Console.WriteLine("服务启动");
            _log.Info("Service is Started");
            //do something
            _timer = new Timer(1000) { AutoReset = true };
            _timer.Elapsed += new ElapsedEventHandler(OnTick);
            _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);
            _timer.Start();
        }
        protected virtual void OnTick(object sender, ElapsedEventArgs e)
        {
            _log.Debug("Tick:" + DateTime.Now.ToLongTimeString());
        }
        public void Dispose()
        {
            Console.WriteLine("服务停止");
            _log.Info("Service is Stopped");
        }
    }

时间: 2024-09-30 17:34:28

可调试Windows服务框架的相关文章

使用Topshelf组件构建简单的Windows服务

很多时候都在讨论是否需要了解一个组件或者一个语言的底层原理这个问题,其实我个人觉得,对于这个问题,每个人都有自己的看法,个人情况不同,选择的方式也就会不同了.我个人觉得无论学习什么,都应该尝试着去了解对应的原理和源码(这里就不要急着吐槽,容我说完).对底层的了解不是为了让你写出类似的东西,让你写也不可能写的出来,重写一个就需要以此修改整个底层结构,了解底层知识只是为了让你可以在写业务代码时,选择合适的方式,以此使底层与业务层配合达到效率最佳.任何一种方式有坏有好,需要合适的选择. 如果觉得楼主以

RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用

RDIFramework.NET框架SOA解决方案(集Windows服务.WinForm形式与IIS形式发布)-分布式应用 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用户和开发者最佳的.Net框架部署方案.该框架以SOA范式作为指导思想,作为异质系统整合与互操作性.分布式应用提供了可行的解决方案. 1.SOA平台简介 1.1.概述 SOA(service-oriented architecture,也叫面向服务的体系结构或面向服务架构)是指为了解决在Inte

使用C语言编写windows服务一般框架

原文:使用C语言编写windows服务一般框架 编写windows服务和编写windows应用程序一样,有一些回调函数必须填写且向windows 服务管理器(service manager)进行注册,否则会导致服务启动失败.因近期写个服务,其中遇到一些有问题,有部分内容想和大家分享一下,请大家指正. windows服务一般框架代码如下: #include <Windows.h> #include <tchar.h> VOID WINAPI ServiceHandler(DWORD

C#使用定时任务框架Windows.TaskSchedule.exe安装控制台应用程序创建的Windows服务

上一篇介绍了利用模板创建.安装以及卸载Windows服务,本篇继续研究这个话题,不过是创建控制台程序,使用Windows.TaskSchedule.exe安装. 为什么要使用控制台程序?--调试简单粗暴.哈哈. 创建控制台应用程序项目后,我们编辑服务主体代码,我们继续向文本文件中写入一句话. 在实现具体逻辑前,我们需要引用Windows.TaskSchedule.Extends.dll,服务类继承 IJob . 1 using System; 2 using System.Configurati

RDIFramework.NET框架SOA解(集Windows服务、WinForm形式和IIS发布形式)-分布式应用程序

RDIFramework.NET框架SOA解决方式(集Windows服务.WinForm形式与IIS形式公布)-分布式应用 RDIFramework.NET,基于.NET的高速信息化系统开发.整合框架,给用户和开发者最佳的.Net框架部署方案. 该框架以SOA范式作为指导思想,作为异质系统整合与互操作性.分布式应用提供了可行的解决方式. 1.SOA平台简单介绍 1.1.概述 SOA(service-oriented architecture,也叫面向服务的体系结构或面向服务架构)是指为了解决在I

DICOM:开源DICOM服务框架DCM4CHE 构建

背景: 前一篇博文DICOM:开源DICOM服务框架DCM4CHE 安装中介绍了一款开源DICOM服务框架DCM4CHE,对于开源项目学习的流程是先下载二进制可执行包安装,然后使用测试.在熟悉了大致的功能服务后,从官网下载源代码进行本地构建(Build),进而从根本上了解开源项目的底层框架设计,为后续修复.扩展做准备.本博文是继DCM4CHE安装后的续篇,讲解如何在本地构建DCM4CHE开源项目,文中尽量做到全面,但是由于刚开始接触J2EE领域,且多半都是自学,因此博文中还留有部分未解问题,如有

c# windows 服务学习

用C#做windows服务变得简单对了===按照下面步骤来就行了用C#创建Windows服务(Windows Services)例子服务功能:这个服务在启动和停止时,向一个文本文件中写入一些文字信息. 第一步:创建服务框架 要创建一个新的 Windows 服务,可以从Visual C# 工程中选取 Windows 服务(Windows Service)选项,给工程一个新文件名,然后点击 确定.你可以看到,向导向工程文件中增加WebService1.cs类:其中各属性的含意是: Autolog 是

采用Windows服务任务调度Quartz.NET

Windows服务使用标准的Csharp编写,任务调度框架采用开源的Quartz.NET.    首先创建Windows服务-JobService     其次创建类库项目-JobLibrary    整体解决方案架构图如下:     在JobLibary中添加Quartz.NET的Nuget包引用,添加之后,vs会自动添加一系列的依赖项,这些依赖项是必须的,别手抖删掉了.            其次再添加Common.Logging.Log4Net1211的Nuget包,他是Log4net的另

分布式服务框架 Zookeeper -- 管理分布式环境中的数据

安装和配置详解 Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置项的管理等.本文将 从使用者角度详细介绍 Zookeeper 的安装和配置文件中各个配置项的意义,以及分析 Zookeeper 的典型的应用场景(配置文件的管理.集群管理.同步锁.Leader 选举.队列管理等),用 Java 实现它们并给出示例代码. 单机模式 单 机安装非常简单,只要获取