.NET Windows Service Template using Timer, Topshelf and Log4Net

Shaun VermaakCOG Lead Engineer
CERTIFIED EXPERT
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.
Published:
Edited by: Andrew Leniart
I have personally never liked the standard Windows Service template that comes with Visual Studio. This article describes how I created a Visual Studio template build on the Topshelf library that I use to develop stable Windows Services.

Introduction


What I like about Topshelf is that it turns a console application into a service installer with a comprehensive set of command-line options for installing, configuring, and running your application as a service. This means you can run it as a console application and easily install it as a service without having to write the code.


I also utilize the Topshelf.Log4Net to enable both service and application logging.


For your convenience, the Visual Studio template can be downloaded from http://blog.ittelligence.com/wp-content/uploads/2018/12/ITtelligence-Windows-Service.zip and simply imported


Creating the template


1) Start Visual Studio and create a Console Application (.NET Framework) Project



2) From Package Manage Console run this command to install Topshelf


Install-Package Topshelf -Version 4.1.0



3) From Package Manage Console run this command to install Log4Net for Topshelf



4) Add a class and call it MyService.cs



5) Add the following code to Service.cs (See comments in the code to understand what it is doing)

Application specific code can be added to the OnTimedEvent method.


using log4net;
using log4net.Config;
using System;
using System.Timers;

namespace WindowsServiceTemplate
{
public class MyService
{
// Setup log4net logger
private static readonly ILog _log = LogManager.GetLogger(typeof(MyService));

// The timer that triggers the event
private Timer _heartbeatTimer = new Timer();

// Boolean to determine if trigger should reoccur
private volatile bool _requestServiceStop = false;

// Interval that event should be triggered at
private readonly long _heartbeatInterval = 10;

public MyService()
{
// Configure log4net from app.config file
XmlConfigurator.Configure();
// Set the timer interval
_heartbeatTimer.Interval = _heartbeatInterval * 1000;
// Set the event that should be triggered
_heartbeatTimer.Elapsed += OnTimedEvent;
// Important! We do not want the event to reoccur unless on trigger has finished
_heartbeatTimer.AutoReset = false;
// Start event
_heartbeatTimer.Start();
}

public void Start()
{
// Event should not stop the timer
_requestServiceStop = false;
// Start event
_heartbeatTimer.Start();
}

public void Stop()
{
// Event should stop the timer
_requestServiceStop = true;
// Stop event
_heartbeatTimer.Stop();
}

private void OnTimedEvent(object source, ElapsedEventArgs e)
{
// Log heartbeat message
_log.Info($"Heartbeat\t{DateTime.Now}");

// Put code here

// Start timer again if no request to stop recieved
if (!_requestServiceStop)
{
_heartbeatTimer.Start();
}
}
}
}


6) Add a class and call it ConfigureService.cs



7) Add the following code to ConfigureService.cs (See comments in the code to understand what it is doing)


using log4net.Config;
using Topshelf;

namespace WindowsServiceTemplate
{
internal static class ConfigureService
{
internal static void Configure()
{
// Configure log4net from app.config file
XmlConfigurator.Configure();

// Service configuration
HostFactory.Run(configure =>
{
configure.Service<MyService>(service =>
{
service.ConstructUsing(s => new MyService());
service.WhenStarted(s => s.Start());
service.WhenStopped(s => s.Stop());
});

// Set account that window service use to run.
configure.RunAsLocalSystem();

// Set service to the log4net
configure.UseLog4Net();

// Service text
configure.SetServiceName("Put your service name here");
configure.SetDisplayName("Put your service display name here");
configure.SetDescription("Put your service description here");
});
}
}
}


8) Edit the App.config file. Essentially we are adding the Log4Net configuration 


<?xml version="1.0" encoding="utf-8"?>

<configuration>
<configSections>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>

<log4net>
<appender name="console" type="log4net.Appender.ConsoleAppender, log4net">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d{ABSOLUTE} [%thread] %-5p %c{1}:%L - %m%n" />
</layout>
</appender>

<appender name="RollingFile" type="log4net.Appender.RollingFileAppender">
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<file value="logs\" />
<datePattern value="'Service-Name-'dd.MM.yyyy'.log'" />
<staticLogFileName value="false" />
<appendToFile value="true" />
<rollingStyle value="Composite" />
<maxSizeRollBackups value="10" />
<maximumFileSize value="5MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%d [%t] %-5p %c - %m%n" />
</layout>
</appender>

<root>
<level value="DEBUG" />
<appender-ref ref="console" />
<appender-ref ref="RollingFile" />
</root>
</log4net>
</configuration>


9) Finally, edit Program.cs


namespace WindowsServiceTemplate
{
internal class Program
{
private static void Main(string[] args)
{
// Start Windows Service
ConfigureService.Configure();
}
}
}


Demo Execution


This is demo execution of application directly from command line.



The following shows a sample of the log file that is generated



The application can easily be installed as a Windows Service with the INSTALL parameter



The installed service



Conclusion


I hope you found this tutorial useful. You are encouraged to ask questions, report any bugs or make any other comments about it below.

 

Note: If you need further "Support" about this topic, please consider using the Ask a Question feature of Experts Exchange. I monitor questions asked and would be pleased to provide any additional support required in questions asked in this manner, along with other EE experts...  

 

Please do not forget to press the "Thumbs Up" button if you think this article was helpful and valuable for EE members.


It also provides me with positive feedback. Thank you!

2
2,912 Views
Shaun VermaakCOG Lead Engineer
CERTIFIED EXPERT
My name is Shaun Vermaak and I have always been fascinated with technology and how we use it to enhance our lives and business.

Comments (0)

Have a question about something in this article? You can receive help directly from the article author. Sign up for a free trial to get started.