Gearchiveerd onder: SW development | Tags: frameworks, Topshelf, windows services
Introduction
Recently I was checking out an open source project called Topshelf (RC2). It’s a small framework that allows you to easily create stable windows services.
The steps below will guide you into creating your own first Topshelf service.
Writing our service code
One of the great thing about Topshelf is that you don’t need to create a windows service application but a console application. Why this is great, you will notice when we execute the code for the first time.
- Start your VisualStudio an create a new console application
- Download the Topshelf library
- Reference all the downloaded files in your console app
- Create a new class named MyTopshelfService, containing the code below
using System;
using System.IO;
using System.Timers;
using log4net;
using log4net.Config;
namespace MyTopshelfTest
{
public class MyTopshelfService
{
readonly Timer timer;
private ILog logger;
public MyTopshelfService()
{
XmlConfigurator.Configure(new FileInfo(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile));
logger = LogManager.GetLogger(typeof(MyTopshelfService));
timer = new Timer(3000)
{
AutoReset = true
};
timer.Elapsed += ExecuteCode;
}
private void ExecuteCode(object sender, ElapsedEventArgs e)
{
logger.Info("Timer elapsed");
}
public void Start()
{
logger.Info("Service started");
timer.Start();
}
public void Stop()
{
logger.Info("Service stopped");
timer.Stop();
}
}
}
Hosting the service in Topshelf
Now that we have a class for Topshelf to host, we can setup Topshelf to actually host our service. To do that we will add configuration code in Program.cs.
using System.IO;
using log4net.Config;
using Topshelf;
using Topshelf.Configuration.Dsl;
namespace MyTopshelfTest
{
class Program
{
static void Main(string[] args)
{
// Configure log4net, reading the TopshelfLog4net.config file
XmlConfigurator.ConfigureAndWatch(new FileInfo(".\\TopshelfLog4net.config"));
// Creating a configuration for Topshelf
var cfg = RunnerConfigurator.New(x =>
{
// hooking up our MyTopshelfService class
x.ConfigureServiceInIsolation<MyTopshelfService>(s =>
{
s.Named("MyTopshelf");
// telling Topshelf how to create our service class
s.HowToBuildService(name => new MyTopshelfService());
// telling Topshelf what method to call to start the service
s.WhenStarted(tc => tc.Start());
// telling Topshelf what method to call to stop the service
s.WhenStopped(tc => tc.Stop());
});
// run the service as a local system user
x.RunAsLocalSystem();
// configure the service not to work unless MSMQ and MSDTC are running (can be seen in services.msc in the tab Dependencies)
x.DependencyOnMsmq();
x.DependsOn("MSDTC");
// set the name and description that will be shown in services.msc
x.SetDescription("Runs a sample service");
x.SetDisplayName("My Top shelf service");
// set the name of your service
x.SetServiceName("TopShelfService");
});
Runner.Host(cfg, args);
}
}
}
Configuring log4net
We’re almost ready to start our service for the first time. All we need now is config for log4net. For that we will add two config files. An app.config file and a TopshelfLog4net.config file. The app.config looks like this:
<configuration> <configSections> <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" /> </configSections> <log4net> <appender name="FileLogger" type="log4net.Appender.FileAppender"> <file value="log.txt" /> <appendToFile value="false" /> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="%message%newline" /> </layout> </appender> <appender name="ConsoleLogger" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="I say: %message%newline" /> </layout> </appender> <root> <level value="INFO" /> <appender-ref ref="ConsoleLogger" /> </root> <logger name="MyTopshelfTest.MyTopshelfService"> <level value="INFO"/> <appender-ref ref="FileLogger"/> </logger> </log4net> </configuration>
The TopshelfLog4net.config looks like this (don’t forget to change the properties of this file so that it is copied to your output folder on build):
<log4net> <appender name="ConsoleLogger" type="log4net.Appender.ConsoleAppender"> <layout type="log4net.Layout.PatternLayout"> <conversionPattern value="Topshelf says: %message%newline" /> </layout> </appender> <root> <level value="INFO" /> <appender-ref ref="ConsoleLogger" /> </root> </log4net>
Debugging with VisualStudio
In the top of this post I mentioned that the great thing about Topshelf is that you can create a console application. This means you can now debug your application straight from VisualStudio. Simply press F5 and your service will run. The result of running the project is the following output:
Topshelf says: Starting Host
Topshelf says: BeforeStart complete
Topshelf says: Starting subordinate service 'MyTopshelf'
I say: Service started
Topshelf says: BeforeStart complete
Topshelf says: The service is running, press Control+C to exit.
I say: Timer elapsed
I say: Timer elapsed
Topshelf says: Control+C detected, exiting.
Topshelf says: Stopping the service
Topshelf says: Stopping sub service 'MyTopshelf'
I say: Service stopped
Topshelf says: AfterStop complete
^C
Installing the service
Ok so it runs as a console application and you can do that from within VisualStudio. Big deal; every console application can do that. I wanted a service, not a console application.
This is how we install our project as a service.
- Open a command prompt and navigate to the output folder of your project
- Type in:
MyTopshelfTest.exe service install
- Run Services.msc
- Notice that our service is installed, the dependencies are set, the name is ok, …
Uninstalling the service
It should be as simple as running this from the cmd prompt:
MyTopshelfTest.exe service uninstall
However that did not do the trick. I found an issue report and patch in the Topshelf project that fixed the problem. Simply download the source. Apply the patch. Build the project and you’re done.
Running multiple instances
Simple:
MyTopshelfTest.exe service install /instance:NewInstanceName
You can now install as many instances of the same service on one server.
What about unhandled exceptions
There is nothing as painful as a service that encounters an unhandled exception and stops running. Topshelf takes care of that for you. To test it, modify the method ExecuteCode in MyTopshelfService to the following:
private void ExecuteCode(object sender, ElapsedEventArgs e)
{
logger.Info("Timer elapsed");
throw new Exception();
}
Run your service from the command prompt. Notice that you will get output and that the timer and service keeps on running.
Conclusion
Is Topshelf the greatest tool ever built and will it dramatically change the amount of effort needed to build a windows service? The answer is no. Would I use and recommend using Topshelf? Absolutely.
Why use Topshelf?
- It is easier to debug then windows services
- It is a lot stabler then some of the services I have seen
- It beats writing this code yourself
- It contains a simple install/uninstall
- It contains a simple install/uninstall for multiple instances
- It’s derived from the service hoster of Mass Transit
Why not to use Topshelf?
- At the time of writing this post it is still a release candidate
- You have to apply a patch and rebuild Topshelf for the uninstaller to work
- A lot of great blog posts have aged information, which leads me to believe the project is still undergoing major changes
Doordat we flink wat werken zelf uitvoeren, hebben we serieus wat geld kunnen uitsparen. Met het uitgespaarde geld zijn we ons dan ofwel meer luxe gaan veroorloven ofwel energiezuinige maatregelen gaan doorvoeren. Één maatregel daarvan is de aanschaf van een zonneboiler. Een schitterend ding waardoor ons warm water mee opgewarmd wordt door de zon (als ze uitzit natuurlijk).
Op woensdag 18 november is Sack bij ons langsgekomen om onze boiler en panelen aan te sluiten. Dat liep allemaal redelijk vlot en willen eigenlijk de installateur bedanken voor zijn inzet. Op zaterdag echter zag ik een klein druppeltje hangen aan de buis die bij de boiler zit. Ik direct gebeld naar Sack. Ze wisten me te vertellen dat ik vooral niet moest panikeren en dat het probleem eenvoudig opgelost kon worden door het koppelstukje wat aan te spannen.
Het koppelstukje zat echter op een voor een standaard sleutel onbereikbare plaats (tussen de muur en de boiler in een ruimte van minder dan 10 cm). Ik dus een drietal winkels afgelopen op zoek naar een sleutel die daar wel bij kon. Uiteindelijk één gevonden. Thuis gekomen spande ik de koppeling wat aan. Veel beter! Maar ‘s avonds, net voor onze vrienden ons een eerste bezoek zouden brengen in ons nieuwe huis, zag ik dat dat nog een beetje lekte. Minder dan de vorige keer, maar toch te veel. Ik pak die sleutel nogmaals om nog wat aan te spannen. Ik span dat aan KWAK! Die buis schiet uit die koppeling. Overal spat er glycol uit die buis en het boeltje blijft uiteraard verder lopen. Snel mijn vrouw geroepen met vodden en emmers. Uiteindelijk die buis terug in de koppeling gestoken en opnieuw aangespannen.
Resultaat: onder de boiler, naast de boiler, voor de boiler, achter de boiler, op mijn kleren, mijn handen, mijn sleutels, mijn vrouw: overal glycol. Nu moet je weten dat glycol een aantal onaangename fysische eigenschappen heeft: vooreerst plakt dat boeltje en ten minstens even erg: het stinkt en blijft stinken eenmaal je het op uw handen gekregen hebt.
Door deze kleine ramp hebben we onze avond niet in het water zien vallen maar in de glycol (haha: flauw mopje). We hadden ons wel een leukere eerste avond in ons nieuw huis voorgesteld.
Wat was mij dat jong. Mochten ze altijd zo moeilijk doen dan zouden er dagdagelijks installaties afgekeurd worden.
We hebben een kraantje in de garage voor “het bijvullen van de regenput”; iets dat we speciaal geplaatst hebben voor de mannen van het VMW. Maar dat kraantje is niet goed: er moet een beluchter op. Het zou maar eens moeten gebeuren dat er een druppeltje in de kraan blijft hangen en dat in dat druppeltje wat bacteriën beginnen te kweken en dan de hoge druk van de waterleiding doorstaan en doorzwemmen om dan vervolgens het ganse waternet te besmetten. Stel je voor! Bon: mijn vader snel achter een kraantje gereden want die gast ging dat dus echt afkeuren: letterlijk zijn woorden.
Dat was echter nog niet alles. Over de boiler had hij ook een vraagje. Wat loopt er door de panelen; antwoord: glycol. Ok, maar welk type glycol; antwoord: wtf? Ik naar Sack gebeld om te vragen welke glycol. Die gast aan de receptie wist da natuurlijk ook niet. Hij te voet naar het magazijn: dat staat niet in dit magazijn, ik zal naar het andere magazijn moeten gaan, zal u over 5 min terugbellen. Zo gezegd zo gedaan; type glycol bla bla bla (wete kik veel wat). Doorverteld aan de keurder en dat was dat. Volgens mij weet die keurder da zelf niet en bovendien heeft dat er niets mee te maken want dat zit in een appart circuit dat afgesloten is van het water en de boiler is gekeurd door de nodige instanties.
Een buitenkraantje op regenwater: ah dat is dus geen drinkwater!!! Dus hij in zijn camionette achter een kleverke om bij de kraan te plakken en aan te geven dat het geen drinkwater is. Stel u voor dat er één of andere idioot langs ons huis komt, het kraantje ziet en denkt: ik heb dorst. Dan dient die gast gegarandeerd een klacht in tegen mij omdat ik geen kleverke gehangen had.
Maar al bij al: keuring was in orde en positief. En de rest vormt een straf verhaal voor later (en waarschijnlijk zal ik het verhaal over de jaren nog hier en daar wat aandikken
.
Schone naam voor regenwater, niet? Hoe dan ook heb ik daar serieus wat werk aan gehad woensdag. Het was verlof en ik dacht: een ganse dag in den bouw: da zal goed vooruit gaan: tarara. Heb bijna de ganse namiddag en avond bezig geweest met het monteren van een regenwaterpomp. Nu hoor ik jullie denken: een pompje aansluiten (want ze hing al aan de muur) wat is daar nu moeilijk aan. Ik zal het u eens gaan vertellen.
Ten eerste moest er een stukje buis komen tussen de pomp en de filter. Dat stukje buis heeft maaaaanden lang in de weg gelegen in ons oude woonst. Echter is het nu plots als ik het nodig heb, spoorloos verdwenen. Minstens een uur ben ik bezig geweest om in alle uithoeken van ons huis dat stukje buis te vinden. Zonder succes; om zot van te worden.
Ik heb dan maar een ander stuk gebruikt dat ik nog liggen had dat er op trekt. Maar nu komt het: het stukje moest niet recht zijn, maar moest met een buiging zijn. Knal! Nog een uur om zeep: miserie dat ik daaraan gehad heb.
En daarmee is de kous af? Nope. Ik steek de pomp in gang: het water spuit uit de collector. Lap een schroef vergeten vast te draaien. Gelukkig was het maar een klein plasje, maar toch.
Tweede poging: alles ziet er goed uit. Maar dan bekijk ik de filter in detail; drup, drup, drup, drup. Bon: koppeling losdraaien wat locktite touw ertussen en terug opdraaien. Pomp opnieuw starten: geen succes. Nog altijd drup, drup, drup. Poging om koppeling terug los te draaien: mislukt, da ding wil niet meer lossen. Uiteindelijk mijn schoonvader gebeld. Die kreeg het ook niet los. Na veel foefelen en wringen en proberen dan toch succes. Blijkt dat we ne “join” ontbreekt. Eentje in gestoken. Boeltje terug samen gestoken: succes. Allé het lek is gestopt, maar nu lekt het elders.
Uiteindelijk dat andere lek ook dicht gekregen (had een stukje omgekeerd aangesloten). Maar ondertussen was het 21u en konden we gaan eten.
Wat een productieve dag!!! De regenwaterpomp is aangesloten. Als alles zo vlot zal lopen, zal ons huis nog een paar keer onder water komen te staan. Laat ons hopen dat de rest vlotter loopt.
Gisteren voor den eerste keer de Qbus zijn werk laten doen. Was de max. Het werkte van den eerste keer. Allé de meeste dingen toch ![]()
Voor het eerst kon er een licht aangestoken worden in ons huisje. Tot voor kort liep ik overal rond met een TL lamp waar ik een stekker aan gehangen had. Nu kon ik gewoon het ganse huis rond lopen en lichten aan steken. Schitterend! Er zaten wel hier en daar nog wat foutjes in mijn programmatie en onzen dimmer werkt nog niet zoals het hoort, maar voor de rest zag alles er prachtig uit. Na 5min programmeren werkten ook de bewegingsmelders, de schakelaars deden wat ze moesten. Een half uurtje later had ik zelfs al een alles uit knop (handig voor bij het verlaten van de woonst). Echt waar: die Qbus daar zal ik nog veel plezier aan beleven. Hopelijk denkt mijn vrouw er ook zo over
‘s Morgens om 6u30 opstaan om vroeg op het werk te staan. ‘s Middags snel eten naar binnen om zo weinig mogelijk tijd te verliezen. ‘s Avonds zo vroeg mogelijk naar huis om dan rechtstreeks naar Menen te rijden en verder te werken aan onzen elektriciteit (en om tijd te besparen: eten in den auto). Donderdag komen ze keuren en er zijn nog een aantal kleine werkjes die moeten afgewerkt worden. Zijn allemaal geen grote zaken, maar ze moeten wel nog gebeuren.
Zal blij zijn dat weekend is. Daarna op naar keuring van water, maar dat lijkt mij minder werk en vooral meer tijd.
Ik verlang tot we erin kunnen. Dan doe ik minstens een week NIETS. ‘t Kan mij allemaal niet schelen wat er nog afgewerkt of in orde gebracht moet worden: platte rust! Klinkt zalig.
Vanaf vrijdag l.l. zijn we officieel eigenaar van ons huis. We wonen ook
niet meer in ons huurhuis. Spijtig genoeg ook nog niet in Menen, maar
bij mijn ouders.
Vanaf vrijdag ll zijn we officieel eigenaar van ons huis. We wonen ook niet meer in ons huurhuis. Spijtig genoeg ook nog niet in Menen, maar bij mijn ouders.
Nu begint het eigenlijk pas echt vooruit te gaan:
- de keuken is geplaatst
- de trap is er
- de schakelaars zijn geïnstalleerd
- de afdekplaatjes worden geplaatst
- de novillon is er
Kortom alle zaken waardoor onzen bouw een huis aan het worden is. En al zeg ik het zelf: het ziet er allemaal zéér goed uit.
Nu alleen nog het basis comfort in orde krijgen. Morgen komen ze de laatste werken uitvoeren voor de gas. Volgende week krijgen we water en ik hoop eerstdaags eindelijk eens aan mijn zekeringskast te kunnen beginnen. Heb al veel voorbereidend werk gedaan voor de keuring ervan, maar daarmee is die kast nog niet af.
Ik hou jullie wel op de hoogte.

