Self installable and runnable service or how to make generic service and console hybrid
Frankly, I thought that one of basic things in windows development, such as “debagability” and “installability” of services, were changed for the last 10 years in development environments. However I was disappointed to discover, that nothing actually changed. You still cannot build easy to debug (in command line) service, which is also can be installed without special additional tools.

Even ServiceName/ServiceNameInstaller trick, is specific for the current assembly and cannot be used if your base class is not the one you are really using. This is not the only approach. Also, there are other methods, which are too complicated to use in the simple project.
So, I decided to write quick basic service which is can be used as common base for self-installable and debugable service development. Let’s start.
First of all we need an abstract service base:
public abstract class ServiceProvider : ServiceBase
Then it identification for derived classes
public static string Name;
public static string ProviderKind;public ServiceProvider(string name) {
ProviderKind = name;
Name = "MagicService." + ProviderKind;
ServiceName = Name;
}
Now method to start it from the hosting application (e.g. command prompt) or run it if installed.
/// <summary>Starts the provider service in interactive mode.</summary>
public void Start(string[] args) {
if (Environment.UserInteractive) {
OnStart(args);
} else {
ServiceBase.Run(this);
}
}
But how to install it? Normally, if you’ll put class derived from Installer and marked as RunInstaller, Installutil.exe can initialize it and install or uninstall the service.
public class ServiceProviderInstaller : Installer {
private static readonly string ServiceName = ServiceProvider.Name;
public ServiceProviderInstaller() {
var processInstaller = new ServiceProcessInstaller {
Account = ServiceAccount.LocalSystem
};var serviceInstaller = new ServiceInstaller {
DisplayName = "Magic Server " + ServiceProvider.ProviderKind + " Provider",
Description = "Process the interface to the Magic service " + ServiceProvider.ProviderKind + " provider.",
ServiceName = ServiceName,
StartType = ServiceStartMode.Automatic,
};
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}
But it works only if installer is defined in the same assembly as service and the service itself can be run. In our case, this is not true. Service is abstract and we allow to run service from any other assembly which is referenced to the base one. So what to do? Simple! Let’s create our own installer. We will create the private instance of installer inside the actual service itself and pass it as additional installer to basic TransactedInstaller. Also we’ll use calling (the actual running) assembly as the service reference.
/// <summary>Installs the provider service in interactive mode.</summary>
public void Install() {
if (Environment.UserInteractive) {
var ti = new TransactedInstaller();
var spi = new ServiceProviderInstaller();
ti.Installers.Add(spi);
var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
var ctx = new InstallContext("", new string[] { path });
ti.Context = ctx;
ti.Install(new Hashtable());
}
}
The same way we’ll do uninstaller
/// <summary>Uninstalls the provider service in interactive mode.</summary>
public void Uninstall() {
if (Environment.UserInteractive) {
var ti = new TransactedInstaller();
var spi = new ServiceProviderInstaller();
ti.Installers.Add(spi);
var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
var ctx = new InstallContext("", new string[] { path });
ti.Context = ctx;
ti.Uninstall(null);
}
}
We almost done, the only problem is Component Designer which wants to be run when you click on any class derived from ServiceBase. I know that Visual Studio developers wanted to do our life easier, but this designer (especially one cannot initialize abstract classes) is very annoying. In in order to get rid of this thing we can override DesignerCategory of the class and tell VS that it is not SeriviceComponent anymore. To do this all we need is one small attribute set on classes
[System.ComponentModel.DesignerCategory("")]
public abstract class ServiceProvider : ServiceBase {
…[RunInstaller(true), System.ComponentModel.DesignerCategory(""), SerializableAttribute]
public class ServiceProviderInstaller : Installer {
Take into account that it should be full reference pass in order to help Visual Studio with fast resolving of references.
We done, let’s put everything together and see what we have and how to use it
/// <summary>Provides service class to respond to service control manager (all responses are defaults).</summary>
[System.ComponentModel.DesignerCategory("")]
public abstract class ServiceProvider : ServiceBase {public static string Name;
public static string ProviderKind;public ServiceProvider(string name) {
ProviderKind = name;
Name = "Magic.Provider." + ProviderKind;
ServiceName = Name;
}/// <summary>Starts the provider service in interactive mode.</summary>
public void Start(string[] args) {
if (Environment.UserInteractive) {
OnStart(args);
} else {
ServiceBase.Run(this);
}
}/// <summary>Installs the provider service in interactive mode.</summary>
public void Install() {
if (Environment.UserInteractive) {
var ti = new TransactedInstaller();
var spi = new ServiceProviderInstaller();
ti.Installers.Add(spi);
var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
var ctx = new InstallContext("", new string[] { path });
ti.Context = ctx;
ti.Install(new Hashtable());
}
}/// <summary>Uninstalls the provider service in interactive mode.</summary>
public void Uninstall() {
if (Environment.UserInteractive) {
var ti = new TransactedInstaller();
var spi = new ServiceProviderInstaller();
ti.Installers.Add(spi);
var path = "/assemblypath=" + Assembly.GetEntryAssembly().Location;
var ctx = new InstallContext("", new string[] { path });
ti.Context = ctx;
ti.Uninstall(null);
}
}
}[RunInstaller(true), System.ComponentModel.DesignerCategory(""), SerializableAttribute]
public class ServiceProviderInstaller : Installer {
private static readonly string ServiceName = ServiceProvider.Name;
public ServiceProviderInstaller() {
var processInstaller = new ServiceProcessInstaller {
Account = ServiceAccount.LocalSystem
};var serviceInstaller = new ServiceInstaller {
DisplayName = "Magic Service " + ServiceProvider.ProviderKind + " Provider",
Description = "Process the interface to the Magic service " + ServiceProvider.ProviderKind + " provider.",
ServiceName = ServiceName,
StartType = ServiceStartMode.Automatic,
};
this.Installers.Add(processInstaller);
this.Installers.Add(serviceInstaller);
}protected override void OnCommitted(IDictionary savedState) {
base.OnCommitted(savedState);
var c = new ServiceController(ServiceName);
c.Start();
}
}
In order to use it, just reference to the hosting assembly and inherit this class
public abstract class SampleService : ServiceProvider {
/// <summary>Creates a new <see cref="SampleService"/> instance.</summary>
public SampleService()
: base("Sample") {
}
}
And run it from command prompt:
class Program {
static void Main(string[] args) {
var p = new SampleService();
if (args.Length > 0) {
if (args[0] == "/i"){
p.Install();
return;
}
if (args[0] == "/u") {
p.Uninstall();
return;
}
}
p.Start(null);
Console.Read();
p.Stop();
}
}
We done. The only remaining thing is how to prevent component designer appearance on derived (SampleService) class. As for now I found no way to do this and asked collective intelligence to help me with it. Once I’ll have an answer I will update it here.
Be good people and have a good day!
UPD (25th Jan): The reason for this strange behavior is cached reference assemblies. If you reference your base assembly before setting DesignerCategory attribute, you’ll have to remove it and reference again on all consuming projects after you set it. Another evidence of Visual Studio developers laziness.
You may also be interested with:
- RSA private key import from PEM format in C#
- Video encoder and metadata reading by using Windows Media Foundation
January 24th, 2012 · Comments (2)
2 Responses to “Self installable and runnable service or how to make generic service and console hybrid”
Leave a Reply
Discover other tags
My tools
- .NET Framework Detector
- Duplicate images finder
- Exchange Security Policy for Windows Mobile Devices Fix
- Gas Price Windows Vista SideBar gadget
- Israel Traffic Information Windows Vista SideBar gadget
- Localization fix for SAP ES Explorer for Visual Studio
- LocTester
- RTL and LTR in Windows Live Writer
- Silverlight controls library
- Snipping tool integration plugin for WLW
- USB FM receiver library
- Vista Battery Saver
- WebCam control for WPF
- Windows Live SkyDrive attachment for Windows Live Writer
- Wireless Migrator
- WPF Virtual Keyboard





January 25th, 2012 at 4:27 am
Also have a look at this blogpost:
http://geekswithblogs.net/BlackRabbitCoder/archive/2010/10/07/c-windows-services-2-of-2-self-installing-windows-service-template.aspx
January 25th, 2012 at 5:22 am
Rudi, of cause i saw it and even referenced in this article. Thank you for your comment