I recently got assigned to a project that implements a messaging architecture for sites to communicate across a geographical deployment. End-users in a site have a Windows Forms interface to work with a local application server, which then drops messages into a number of MSMQ queues in the same machine depending on the type of message: batch or urgent. A single Windows Service program is written to process these queues periodically (30 minutes for batch, 10 seconds for urgent) and forward them to a central site for further distribution and archival.
The service has a problem: it doesn't always successfully Stop as and when the administrator requires so, and the process has to be killed manually. It is not a show-stopper bug, but nonetheless irritating. When I asked to have a look at the service's OnStop() event method, it turned out something like this: protected override void OnStop()
Custom threads were written and spawn to handle each specific MSMQ queue, and it is unknown where it usually stalls, as this is not a high-priority issue that warrants time for fixing. I however shared the way I wrote Windows Services when I had to accomplish similar chores previously: protected override void OnStart(string args)
this.daemon = Daemon.GetInstance();
protected override void OnStop()
this.daemon = null;
Just two things I'd like to highlight, one visible from the above code, and another not exposed here because the code sits inside that of the real service class, Daemon.
1. I am a pretty deep believer in layer architecture, and as such consider Windows Service a presentation layer
. Even though there is no human user actively operating it (call it an "operating layer" if you want then). Application and domain logic are always placed into a (class library) project that has absolutely no knowledge what type of presentation client is in use. As much as possible, it shouldn't have to care.
If you haven't already found it apparent in my implementation, I should then state it explicitly here that my first steps to Windows Service development is always writing it in a Windows Forms
project. It is tremendously easy to put Start/Stop buttons in a form and hook it to the respective methods of my Daemon object (secured as a Singleton), and I have a live interactive "service" to test with. Once stable enough to operate "unseen", the Windows Service project just reference the daemon's assembly and call the very same methods and do nothing else.
2. As per .NET Framework recommendations, spawn custom threads only if you really need to, and only when the built-in ThreadPool facility does not suffice. Since a large number of services just do work on intervals or schedules, the adequacy of System.Timers.Timer
is more than enough with such. Using these objects in the Daemon just means setting up event handlers for their timer elapse. While one still needs to remain conscious of potential access conflicts between threads, the elimination of manual thread management, letting the CLR take care of abortion and cleanup of the timer threads, and concerning only when to start or stop the timers, should simplify development worries.