参考:
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