Windows service - how to make name configurable
I have written a windows service in C#. The requirement at the beginning was that I should be running only one instance of that service, but that has changed and now I need multiple instances. This is why I need to change the service name according to the configuration file.
What would be the best way to make it register with the correct name ? Should I write another tool to do it? Can I just read the name from App.config file and set it in the service and installer classes ac开发者_Go百科cordingly ?
PS> I do not really understand how that thing with names work - one should set names in service and installer classes, but then when installing with installutil.exe or even powershell new-service the name also should be specified. Does that have to be the same? Or one overrides another?
You can simply read it from the app.config and set it in the installer classes.
Normally, a class that inherits from Installer
is automatically created. It contains a member of type System.ServiceProcess.ServiceInstaller
, most likely named serviceProcessInstaller1
. This has a property ServiceName
you can set. Additionally, you need to set the ServiceName
property of the ServiceBase
derived class to the same value.
In a default implementation, these are set to constant values in the respective InitializeComponent
methods, but there is no reason to stick with this. It can be done dynamically without problems.
I though I'd add my 2 cents since I ran into this. I have a file called "ProjectInstaller.cs" with designer and resources under it. Opening it up in design shows MyServiceInstaller and MyProjectInstaller as items on the design surface. I was able to change the names in the ProjectInstaller()
constructor, and manually loaded the config file from the module directory:
public ProjectInstaller()
{
InitializeComponent();
var config = ConfigurationManager.OpenExeConfiguration(this.GetType().Assembly.Location);
if (config.AppSettings.Settings["ServiceName"] != null)
{
this.MyServiceInstaller.ServiceName = config.AppSettings.Settings["ServiceName"].Value;
}
if (config.AppSettings.Settings["DisplayName"] != null)
{
this.MyServiceInstaller.DisplayName = config.AppSettings.Settings["DisplayName"].Value;
}
}
In the same vein as Jason Goemaat's answer, this is how you would loop through all available installers in your project, which saves you the time of being sure you added each new service to this class. In my project, I have a total of 12 services (and we add a new one here and there), and we wanted them grouped together by the instance name, so SVC (Instance 1) Service XX and SVC (Instance 1) Service YY were next to each other when viewed in the services snap-in console.
public ProjectInstaller()
{
InitializeComponent();
var config = ConfigurationManager.OpenExeConfiguration(this.GetType().Assembly.Location);
string instanceName = config.AppSettings.Settings["Installer_NamedInstanceName"].Value;
string instanceID = config.AppSettings.Settings["Installer_NamedInstanceID"].Value;
bool usesNamedInstance = !string.IsNullOrWhiteSpace(instanceName) && !string.IsNullOrWhiteSpace(instanceID);
if (usesNamedInstance)
{
foreach (var installer in this.Installers)
{
if (installer is ServiceInstaller)
{
var ins = (ServiceInstaller)installer;
ins.ServiceName = ins.ServiceName + "-" + instanceID;
// Want the service to be named SVC (Instance Name) Audit Log Blah Blah Service
ins.DisplayName = ins.DisplayName.Replace("SVC ", "SVC (" + instanceName + ") ");
}
}
}
}
HOWEVER, there is something else that you need to do - When initializing the service, you must also change the service name, otherwise you'll get an error along the lines of "The executable doesn't implement the service". I did this by implementing the following code in Program.cs
:
internal static void HandleCustomServiceName(ServiceBase sbase)
{
if (!string.IsNullOrWhiteSpace(customInstanceName))
{
sbase.ServiceName = sbase.ServiceName + "-" + customInstanceName;
}
}
Then, in the constructor of each service:
public SystemEventWatcher()
{
InitializeComponent();
Program.HandleCustomServiceName(this);
}
I've upvoted Jason's answer for paving the way to implementing this in my own project. Thanks!
精彩评论