So I am flying along using all the tools and tricks while writing a windows service (aka NT service) using Visual Studio. Life is good. I want a windows service so the IDE has a templated project for it. The result is not to complicated. The templated windows service project leverages the Dot Net Framework which wraps all the difficult Win32 stuff.
Of course I will need to install my application and to say installing a windows service is difficult is an understatement. But once again there is a nice templated class I can add to my windows service project that leverages an excellent Dot Net Framework class. Again the result is clean, although with a small hiccup. There does not seem to be an option or flag or method to say 'start this service on install'.
This is a good example of Technical Debt. The installer for services is not complete. So I have to pay down this particular debt to get what I want. I am still way ahead and the IDE is still real useful.
If I double click the Service Installer object it created for me, the IDE creates the event handler 'sampleServiceInstaller_AfterInstall'. So all I need to do is get a reference to my service which the Framework class 'System.ServiceProcess.ServiceController' makes real easy and set its state.
There is a gotcha here if you try to start it when it is started, it considers that an error. Also if it has any difficulty at all in running this class tends to handle the problem by throwing an exception. Partly this is because the underlying Win32 api's it wraps behave this way. So for now I have an empty try catch handler. Which means I have piled up a bit of technical debt.
Now on to starting the service. The 'ServiceController' class has a service variable and if we create a switch case using the snippet for switch we get most of the following code:
private void sampleServiceInstaller_AfterInstall(object sender, InstallEventArgs e)
{
try
{
// serviceController
//
System.ServiceProcess.ServiceController serviceController = new ServiceController();
serviceController.ServiceName = "SampleService";
serviceController.Refresh();
switch (serviceController.Status)
{
case ServiceControllerStatus.ContinuePending:
break;
case ServiceControllerStatus.PausePending:
break;
case ServiceControllerStatus.Paused:
break;
case ServiceControllerStatus.Running:
break;
case ServiceControllerStatus.StartPending:
break;
case ServiceControllerStatus.StopPending:
break;
case ServiceControllerStatus.Stopped:
serviceController.Start();
break;
default:
break;
}
}
catch (Exception ex)
{
// Unhandled exception
}
}
There are a lot of states there. The one I am interested in is the Stopped state so I make the call
to start the service there.
Which leaves open the other states. I have several states that have pending in their name. What needs to happen is the program needs to wait and query the status again. The pending action will resolve one way or another. But the context makes this difficult. We are in an installer (technically we are doing post install actions, but the user will perceive it as part of the installer).
So we do not want to loop and keep waiting for this to end. We could launch a thread to watch over this situation. But then we face having the installer appear to exit while a thread is still working. It seems no matter where I turn I am going to pile up some technical debt.