可调试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 = ; 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().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="/>--> <!--每个日志文件的最大大小--> <!--可用的单位: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="/> <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="/> <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="/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%thread"/> </layout> </parameter> <parameter> <parameterName value="@log_level"/> <dbType value="String"/> <size value="/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%level"/> </layout> </parameter> <parameter> <parameterName value="@logger"/> <dbType value="String"/> <size value="/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%logger"/> </layout> </parameter> <parameter> <parameterName value="@message"/> <dbType value="String"/> <size value="/> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%message"/> </layout> </parameter> <parameter> <parameterName value="@exception"/> <dbType value="String"/> <size value="/> <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); } }
这里用Path.Combine方法更好:
FileInfo fi = new FileInfo(Path.Combine(AppDomain.CurrentDomain.BaseDirectory , "log4net.config")); XmlConfigurator.ConfigureAndWatch(fi);
更新后的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 = ) { 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"); } }
可调试Windows服务框架的更多相关文章
- Topshelf一个用于使用.NET构建Windows服务框架
1 Topshelf是什么? Topshelf是用于托管使用.NET框架编写的Windows服务的框架.服务的创建得到简化,从而使开发人员可以创建一个简单的控制台应用程序,可以使用Topshelf将其 ...
- Windows服务框架与服务的编写
从NT内核开始,服务程序已经变为一种非常重要的系统进程,一般的驻守进程和普通的程序必须在桌面登录的情况下才能运行,而许多系统的基础程序必须在用户登录桌面之前就要运行起来,而利用服务,可以很方便的实现这 ...
- 使用Topshelf组件构建简单的Windows服务
很多时候都在讨论是否需要了解一个组件或者一个语言的底层原理这个问题,其实我个人觉得,对于这个问题,每个人都有自己的看法,个人情况不同,选择的方式也就会不同了.我个人觉得无论学习什么,都应该尝试着去了解 ...
- 如何使用Topshelf与.NET泛型主机建立Windows服务
1 前置阅读 在阅读本文章之前,你可以先阅读: Topshelf一个用于使用.NET构建Windows服务框架 2 使用 2.1 创建应用程序 首先,创建一个新的控制台应用程序并从nuget获取Top ...
- RDIFramework.NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用
RDIFramework.NET框架SOA解决方案(集Windows服务.WinForm形式与IIS形式发布)-分布式应用 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架 ...
- 使用C语言编写windows服务一般框架
原文:使用C语言编写windows服务一般框架 编写windows服务和编写windows应用程序一样,有一些回调函数必须填写且向windows 服务管理器(service manager)进行注册, ...
- NET框架SOA解决方案(集Windows服务、WinForm形式与IIS形式发布)-分布式应用
NET框架SOA解决方案(集Windows服务.WinForm形式与IIS形式发布)-分布式应用 RDIFramework.NET,基于.NET的快速信息化系统开发.整合框架,给用户和开发者最佳的.N ...
- Topshelf 一个简化Windows服务开发的宿主服务框架
Topshelf是 基于.net框架开发的宿主服务框架.该框架简化了服务的创建,开发人员只需要使用 Topshelf编写一个控制台程序,就能安装为Windows服务.之所以这样原因非常简单:调试一个控 ...
- RDIFramework.NET框架SOA解(集Windows服务、WinForm形式和IIS发布形式)-分布式应用程序
RDIFramework.NET框架SOA解决方式(集Windows服务.WinForm形式与IIS形式公布)-分布式应用 RDIFramework.NET,基于.NET的高速信息化系统开发.整合框架 ...
随机推荐
- HDU 4355 Party All the Time (三分求极值)
题意:给定x轴上有n个点,每一个点都有一个权值,让在x轴上选一个点,求出各点到这个点的距离的三次方乘以权值最小. 析:首先一开始我根本不会三分,也并没有看出来这是一个三分的题目的,学长说这是一个三分的 ...
- Swift中的闭包(Closure)[转]
闭包在Swift中非常有用.通俗的解释就是一个Int类型里存储着一个整数,一个String类型包含着一串字符,同样,闭包是一个包含着函数的类型.有了闭包,你就可以处理很多在一些古老的语言中不能处理的事 ...
- day05(Object,tostring(),equals(),System,Date,SimpleDateFormat,拆装箱,正则表达式)
Object类, 是所应类的父类: 拥有自己的方法:常用的 红颜色标记的为常用的方法 toString() 用法:打印对象的地址值 getClass() 获取当前类的字节码文件getName() ...
- Linux中的LVM和软RAID
在实际工作中,会经常碰到所给的服务器硬盘容量太小,而实际的应用软件中却需要一个容量较大的分区进行数据存储等,除了通过硬件RAID卡来实现合并多硬盘外,其实我们也可以通过软件的方式来实现. 实验 ...
- RelativeLayout中最底的View一个View.layout_marginBottom无效
处理一个Dialog,发现RelativeLayout布局下最后一个View的layout_marginBottom会失效. 效果图见: 解决方法为: 在最底或最右的组件后面再加个View吧... 这 ...
- spring mvc静态资源请求和<mvc:annotation-driven>
自己看了官方文档,也到网上查了下,目前理解如下: <mvc:annotation-driven/>相当于注册了DefaultAnnotationHandlerMapping和Annotat ...
- 转 UNIGUI安装教程、使用例子
转 UNIGUI安装教程.使用例子 http://my.oschina.net/u/582827/blog/203429?p={{currentPage-1}} 转 uniGui安装教程.使用例子 发 ...
- 使用 X-Frame-Options 防止被iframe 造成跨域iframe 提交挂掉
Refused to display 'http://www.***.com/login/doLogin.html' in a frame because it set 'X-Frame-Optio ...
- 微信公众平台如何与Web App结合?
Web App简而言之就是为移动平台而优化的网页,它可以表现得和原生应用一样,并且克服了原生应用一些固有的缺点.一般而言Web App最大的入口是浏览器,但现在微信公众平台作为新兴的平台,结合其内置浏 ...
- webapi 用腾讯云手机短信验证码
#region 验证码相关 public class SmsSDK { #region 短信发送的必要参数 /// <summary> /// appId /// </summary ...