So we've produced a windows service to feed data to our client application and everything is going great. The client has come up with a fun configuration request that requires two instances of this service running on the same server and configured to point at separate databases.
So far I haven't been able to get this to happen and was hoping my fellow stackoverflow members might be able to give some hints as to why.
Current setup:
I've set up the project that contains the windows service, we'll call it AppService from now on, and the ProjectInstaller.cs file that handles custom installation steps to set the service name based on a key in the App.config like so:
this.serviceInstaller1.ServiceName = Util.ServiceName;
this.serviceInstaller1.DisplayName = Util.ServiceName;
this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
In this case Util is just a static class tha tloads the service name from the config file.
From here forward I have tried two different ways to get both services installed and both have failed in an identical way.
The first way was to simply install the first copy of the service, copy the installed directory and renamed it, and then ran the following command after modifying the app config to change the desired service name:
InstallUtil.exe /i AppService.exe
When that didn't work I tried to create a second installer project, edited the config file and built the second installer. When I ran the installer it worked fine but the service did not show up in services.msc so I ran the previous command against the second installed code base.
Both times i received the following output from InstallUtil (relevant parts only):
Running a transacted installation.
Beginning the Install phase of the installation.
Installing service App Service Two... Service App Service Two has been successfully installed. Creating EventLog source App Service Two in log Application...
An exception occurred during the Install phase. System.NullReferenceException: Object reference not set to an instance of an object.
The Rollback phase of the installation is beginning.
Restoring event log to previous state for source App Service Two. Service App Service Two is being removed from the system... Service App Service Two was successfully removed from the system.
The Rollback phase completed successfully.
The transacted install has completed. The installation failed, and the rollback has been performed.
Sorry for the long winded post, wanted to make sure there is enough relevant information. The piece that so far has me stumped is that it states that the installation of the service completes successfully and its only after it goes to create the EventLog source that the NullReferenceException seems to get thrown. So if anyone knows what I'm doing wrong or has a better approach it would be much appreciated.
What I've done to make this work is to store the service name and display name in an app.config for my service. Then in my installer class, I load the app.config as an XmlDocument and use xpath to get the values out and apply them to ServiceInstaller.ServiceName and ServiceInstaller.DisplayName, before calling InitializeComponent(). This assumes you're not already setting these properties in InitializeComponent(), in which case, the settings from your config file will be ignored. The following code is what I'm calling from my installer class constructor, before InitializeComponent():
I don't believe reading the configuration file directly from ConfigurationManager.AppSettings or something similar will work as when the installer runs, it's run in the context of InstallUtil.exe, not your service's .exe. You may be able to do something with ConfigurationManager.OpenExeConfiguration, however in my case, this didn't work as I was trying to get at a custom configuration section that was not loaded.
You can run multiple versions of the same service by doing the following:
1) Copy the Service executable and config to its own folder.
2) Copy Install.Exe to the service executable folder (from .net framework folder)
3) Create a config file called Install.exe.config in the service executable folder with the following contents (unique service names):
4) Create a batch file to install the service with the following contents:
5) While your there, create an uninstall batch file
EDIT:
Note sure if I missed something, here is the ServiceInstaller Class (adjust as required):
This solution worked for me.
Just to improve perfect answer of @chris.house.00 this, you can consider following function to read from your app settings: