Last time, we created an IService interface to abstract our application logic away from its hosting mechanism (console or service). In a real-world scenario, an implementor of IService may need to do substantial work in its constructor (such as connecting to external services and loading data). Let’s simulate this by having our demo implementation sleep for a while in its constructor:

If we install the service and run it via services.msc, the “starting” dialog will appear:

However, after a while, the dialog will close and the following error message will appear, informing us that the service did not respond to the start or control request in a timely fashion:

Upon dismissing the dialog, you will notice that the service is not running. It was forcibly terminated by the service control manager. Now, let’s repeat the experiment, but this time we will move the sleep statement to the start method:

Using this approach, the “starting” dialog will linger for a minute, but then it will disappear, leaving the service in the “started” state. The lesson to learn from this experiment is that the service control manager expects you to hand over control (via ServiceBase.Run) somewhat promptly, but it has basically unlimited patience for your “on start” handler. To ensure that our services do not time out, we should do our best to ensure that any time consuming work is deferred until the “on start” handler. While we are at it, we should encapsulate the dual-mode service startup logic that we wrote in our main method last time. To begin, we will factor out our startup logic into a new static class, BasicServiceStarter:

With this approach, we are still constructing MyService before the call to ServiceBase.Run. However, with minor changes and clever use of generic type constraints, we can easily have our new infrastructure defer the construction of MyService until the ServiceBase.OnStart method is called:

In this approach, BasicServiceStarter.Run takes the service as a generic type argument, rather than as an instance argument. By passing the type to BasicService as generic type argument as well, we can construct the service inside the BasicService.OnStart method. This of course requires another tweak to our main method:

With this approach, the constructor of MyService can take as long as it wants, and we will avoid incurring the wrath of the service control manager, because we don’t invoke the constructor until OnStart is called.

We're here to provide you with the information you need to be an awesome "DevOpeler" in a Windows environment - from concepts, to how-to articles, to specific products that will make your life easier and your enterprise more successful.