Click here to Skip to main content
15,868,016 members
Articles / Programming Languages / C#
Article

Multiple Instance .NET Windows Service

Rate me:
Please Sign up or sign in to vote.
4.77/5 (51 votes)
15 Nov 2007CPOL5 min read 374K   6.5K   71   97
This article describes how to implement a .NET Windows Service that can have multiple instances installed on the same machine.

Introduction

It's not readily apparent how to install a Windows Service multiple times on a single machine. At first glance, it seems like it's not supported by the operating system. When attempting to install a second instance of a service using InstallUtil on the same machine, you'll likely be presented with the following message:

Screenshot - InstallUtilMultipleServiceException.gif

Fortunately, there is a way around this. If you have a need to install multiple instances of the same service, then please read on.

The Problem

When reviewing the ServiceInstaller class using Reflector, you'll see it does a Win32 call to CreateService in the ServiceInstaller.Install method. CreateService will return the ERROR_DUPLICATE_SERVICE_NAME return code whenever the method is called with a serviceName or displayName parameter that matches that of an already installed service. This is what causes the Win32Exception to be thrown in the above window and ultimately causes the second install to fail. You can view the complete documentation on the CreateService function here.

To get around this, all that needs to be done is to dynamically set the service name during installation and service startup to a known value and to make sure that value is different for each instance of the service you install.

The Solution

All that needs to be done is to make sure that the name of each service installation instance is unique. Then you need to have copies of installation directories for each service instance you'd like to have. Finally, you need to be able to set the service name dynamically during installation and startup. This will be facilitated using the app.config file.

Storing the Service Name

The cleanest and most readily available way of providing the service name is to use an app.config file for your service and create an appSettings key that can be read from to set the service name. An app.config file needs to be added to your solution if one doesn't already exist to store the unique service name in order to dynamically set the ServiceInstaller.ServiceName and the ServiceBase.ServiceName to the same service name value you added to your configuration.

Alternately, we could have provided a custom command line switch to the InstallUtil during installation like InstallUtil /i /servicename="Service Instance 1" MultipleInstanceService.exe. Then that would allow us to pull the servicename property off the Context.Parameters collection during installation. The reason I chose not to recommend this option is that it does not allow us to reliably set the ServiceBase.ServiceName to the exact value that was provided during installation. Someone installing the service could mistype the servicename they provide during install which wouldn't match the value set in configuration.

XML
<configuration>
    <appSettings>
        <add key="ServiceName" value="Service Instance 1"/>
    </appSettings>
</configuration>

Reading the Service Name from Configuration

The app.config file needs to be accessible during the installation of the service as well as when the service starts up. This is cake while the service is running because the configuration file loaded into the configuration system will be the configuration file for your service. It is a little trickier during installation. The reason why it's more difficult during installation is the fact that InstallUtil which is used to install the service does not run in the same execution path your service does, instead it runs at <system drive>:\windows\Microsoft.NET\Framework\v2.0.50727\. So, we need to find another way to access the configuration file for our service.

The easiest and most reliable way I found to do this is to use the Assembly class in the System.Reflection namespace. Using the Location property of the Assembly, we can identify the installation path of the Windows Service and open that services configuration file as shown below.

C#
private string GetConfigurationValue(string key)
{
    Assembly service = Assembly.GetAssembly(typeof(MultipleInstanceInstaller));
    Configuration config = ConfigurationManager.OpenExeConfiguration(service.Location);
    if (config.AppSettings.Settings[key] != null)
    {
        return config.AppSettings.Settings[key].Value;
    }
    else
    {
        throw new IndexOutOfRangeException
            ("Settings collection does not contain the requested key: " + key);
    }
}

Now that the app.config file can be read during installation, all we need to do is create an instance of a ServiceInstaller class and set the ServiceName property to the value we have stored in configuration. Using the method in the code snippet above, this is an easy task.

C#
ServiceInstaller installer = new ServiceInstaller();
installer.ServiceName = GetConfigurationValue("ServiceName");
return installer;

Now that we've created our ServiceInstaller class instance as described above and added it to the Installers collection of our root System.Configuration.Install.Installer class marked with the RunInstaller(true) attribute of our Windows Service project, there's only one last thing to do. We need to set the ServiceBase.ServiceName of our service class instance when the service starts up. This time we'll use the ConfigurationManager class directly to read our services configuration file in the constructor of our service instance.

C#
public MultipleInstanceService()
{
    InitializeComponent();

    this.ServiceName = ConfigurationManager.AppSettings.Get("ServiceName");
}

Now all that is left to do is install this service at a few different locations on the same machine. Adding a new instance of the service becomes as simple as a copy/paste of the service installation directory and change of some configuration values.

Copy of debug to model multiple directory installations.

I just made a copy of the debug directory in order to have another service installation directory. It'd be better to use a different convention for directory names than I've done here, but this is just for illustration. Now that there are two copies of the same service on your machine all that is left to do is install them.

Multiple service instances

From a command prompt, you'll need to use InstallUtil to install both instances of your service. For instructions on how to use InstallUtil, see Installer Tool (InstallUtil.exe). Once you're done installing the service instances, you'll have something like the services console above where Service Instance 1 and Service Instance 2 are created from the same executable, only installed from different directory locations with a different service name.

Conclusion

Being able to use the services application model to manage your application's runtime is an invaluable asset. Using the pattern I've presented, you can now leverage multiple instances of the same service on a single machine with little effort. Hopefully you'll find this method as useful as I did.

Updated

  • 2007.11.16 - Changes based on reader comments - removed sample scenario and replaced with why you're not able to install a second instance of the service. Added alternate way of passing service name into the installer class.
  • 2007.11.15 - Original post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Service being installed twice [modified] Pin
Wil Peck20-Jan-09 8:59
Wil Peck20-Jan-09 8:59 
GeneralRe: Service being installed twice [modified] Pin
Member 330643820-Jan-09 9:04
Member 330643820-Jan-09 9:04 
GeneralRe: Service being installed twice Pin
Member 330643820-Jan-09 9:37
Member 330643820-Jan-09 9:37 
GeneralRe: Service being installed twice Pin
Wil Peck20-Jan-09 9:39
Wil Peck20-Jan-09 9:39 
GeneralRe: Service being installed twice Pin
Member 330643820-Jan-09 9:52
Member 330643820-Jan-09 9:52 
QuestionMultiple Instance of Window Serivce - !!!! U R G E N T !!!! Pin
Member 43935517-Nov-08 10:28
Member 43935517-Nov-08 10:28 
AnswerRe: Multiple Instance of Window Serivce - !!!! U R G E N T !!!! Pin
Wil Peck17-Nov-08 12:33
Wil Peck17-Nov-08 12:33 
QuestionAlternate? Pin
Eduard Gomolyako15-Nov-07 21:22
Eduard Gomolyako15-Nov-07 21:22 
When you create your NT service at Visual Studio 2005, you need to specify Installer class that derived from System.Configuration.Install.Installer and have attribute [RunInstaller(true)].

In your part of this partial class you can override Install and Uninstall method as follows:

		public override void Install(IDictionary stateSaver)<br />
		{<br />
			if (Context.Parameters["servicename"] != null)<br />
				serviceInstaller.ServiceName = Context.Parameters["servicename"];<br />
<br />
			base.Install(stateSaver);<br />
		}<br />
<br />
		public override void Uninstall(IDictionary savedState)<br />
		{<br />
			if (Context.Parameters["servicename"] != null)<br />
				serviceInstaller.ServiceName = Context.Parameters["servicename"];<br />
<br />
			base.Uninstall(savedState);<br />
		}<br />


Then when you call installUtil to install your service you need specify /servicename="Name of your service" in command line.

Best, Ed.

AnswerRe: Alternate? Pin
Wil Peck16-Nov-07 4:25
Wil Peck16-Nov-07 4:25 
GeneralRe: Alternate? Pin
Eduard Gomolyako16-Nov-07 10:10
Eduard Gomolyako16-Nov-07 10:10 
GeneralRe: Alternate? Pin
Wil Peck16-Nov-07 10:14
Wil Peck16-Nov-07 10:14 
GeneralRe: Alternate? Pin
Eduard Gomolyako16-Nov-07 10:23
Eduard Gomolyako16-Nov-07 10:23 
GeneralRe: Alternate? Pin
Wil Peck16-Nov-07 10:31
Wil Peck16-Nov-07 10:31 
GeneralRe: Alternate? Pin
Eduard Gomolyako16-Nov-07 10:40
Eduard Gomolyako16-Nov-07 10:40 
GeneralRe: Alternate? Pin
Wil Peck16-Nov-07 10:43
Wil Peck16-Nov-07 10:43 
GeneralRe: Alternate? Pin
Eduard Gomolyako16-Nov-07 10:55
Eduard Gomolyako16-Nov-07 10:55 
GeneralRe: Alternate? Pin
Wil Peck16-Nov-07 11:01
Wil Peck16-Nov-07 11:01 
GeneralRe: Alternate? Pin
JNeck19-Nov-07 3:44
JNeck19-Nov-07 3:44 
GeneralRe: Alternate? Pin
Wil Peck19-Nov-07 4:58
Wil Peck19-Nov-07 4:58 
GeneralRe: Alternate? Pin
dave_kinmond19-Nov-07 22:28
dave_kinmond19-Nov-07 22:28 
GeneralRe: Alternate? Pin
Wil Peck20-Nov-07 5:44
Wil Peck20-Nov-07 5:44 
AnswerRe: Alternate? Pin
DarrenMB12-Sep-08 11:57
DarrenMB12-Sep-08 11:57 
GeneralRe: Alternate? Pin
Wil Peck12-Sep-08 12:01
Wil Peck12-Sep-08 12:01 
GeneralRe: Alternate? Pin
DarrenMB12-Sep-08 14:28
DarrenMB12-Sep-08 14:28 
GeneralError Handling would solve this. Pin
Stephen Brannan15-Nov-07 8:03
Stephen Brannan15-Nov-07 8:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.