Windows Service Installer for .NET
Back in my Delphi days the Windows Service template provided a nice feature whereby you could supply command-line arguments to install and uninstall the built service, however the .NET side has always sadly lacked such a feature and usually defers to the installutil.exe which you have to find and call yourself.
Whilst creating a new Windows Service in C# recently I wrote a class which helps alleviate this issue and supports a number of features such as silently installing and uninstalling the service and generating native images using NGEN. You can see the kinds of arguments you can pass below.

The code for this class can be found below. You will need to put the class directly in the service project (i.e where Program.cs is) in order for it to function properly and you must also have a project installer for the service, however if you are already using installutil.exe then this shouldn’t be a problem for you.
using System;
using System.Collections.Generic;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.ServiceProcess;
using System.Text;
using System.Windows.Forms;
namespace Storm.API.Logging
{
public static class ServiceInstaller
{
public static bool ProcessArguments(string serviceName, string[] args)
{
string parms = String.Concat(args);
bool silent = false;
bool ngen = false;
bool ui = Environment.UserInteractive;
if (parms.Contains("/silent") || parms.Contains("/s") || parms.Contains("--silent") || parms.Contains("-s")) silent = true;
if (parms.Contains("/ngen") || parms.Contains("/n") || parms.Contains("--ngen") || parms.Contains("-n")) ngen = true;
if (parms.Contains("/install") || parms.Contains("/i") || parms.Contains("--install") || parms.Contains("-i")) {
try {
// Check service isn't already installed
if (IsServiceInstalled(serviceName)) {
// If interactive, report
if (!silent && ui) MessageBox.Show("Service already appears to be installed.","Install Service",MessageBoxButtons.OK,MessageBoxIcon.Warning);
// Return
return true;
}
// Install service
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
// If NGEN, perform generation
if (ngen) NativeImage(true);
// If interactive, report
if (!silent && ui) MessageBox.Show("Service was successfully installed.","Install Service",MessageBoxButtons.OK,MessageBoxIcon.Information);
} catch (Exception e) {
// Show error
if (!silent && ui) MessageBox.Show("Service could not be installed due to an exception:\r\n\r\n" + e.Message,"Install Service",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
// Return
return true;
} else if (parms.Contains("/uninstall") || parms.Contains("/u") || parms.Contains("--uninstall") || parms.Contains("-u")) {
try {
// Check service isn't already uninstalled
if (!IsServiceInstalled(serviceName)) {
// If interactive, report
if (!silent && ui) MessageBox.Show("Service already appears to be uninstalled.","Uninstall Service",MessageBoxButtons.OK,MessageBoxIcon.Warning);
// Return
return true;
}
// Uninstall service
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
// If NGEN, perform generation
if (ngen) NativeImage(false);
// If interactive, report
if (!silent && ui) MessageBox.Show("Service was successfully uninstalled.","Uninstall Service",MessageBoxButtons.OK,MessageBoxIcon.Information);
} catch (Exception e) {
// Show error
if (!silent && ui) MessageBox.Show("Service could not be uninstalled due to an exception:\r\n\r\n" + e.Message,"Uninstall Service",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
// Return
return true;
} else if (parms.Contains("/?") || parms.Contains("/help") || parms.Contains("--help") || parms.Contains("-h")) {
// Get service filename
string filename = Path.GetFileName(Assembly.GetExecutingAssembly().Location);
// Show help
if (!silent && ui) {
StringBuilder builder = new StringBuilder();
builder.AppendLine("Usage: " + filename + " [options]");
builder.AppendLine();
builder.AppendLine("/install, /i, --install, -i");
builder.AppendLine(" Installs the service onto the current computer.");
builder.AppendLine();
builder.AppendLine("/uninstall, /u, --uninstall, -u");
builder.AppendLine(" Uninstalls the server from the current computer.");
builder.AppendLine();
builder.AppendLine("/silent, /s, --silent, -s");
builder.AppendLine(" Marks the install/uninstall process as silent, no message dialogs will be displayed.");
builder.AppendLine(" Must be used in combination with the install or uninstall switches.");
builder.AppendLine();
builder.AppendLine("/ngen, /n, --ngen, -n");
builder.AppendLine(" Instructs the .NET Framework to generate a native image using NGEN.");
builder.AppendLine(" Must be used in combination with the install or uninstall switches.");
builder.AppendLine();
builder.AppendLine("/help, /?, --help, -h");
builder.AppendLine(" Displays this help and usage dialog.");
builder.AppendLine();
MessageBox.Show(builder.ToString(),"Service Help",MessageBoxButtons.OK,MessageBoxIcon.Information);
}
// Return
return true;
} else {
// Return
return false;
}
}
private static bool IsServiceInstalled(string serviceName)
{
// Get a list of current services
ServiceController[] services = ServiceController.GetServices();
// Look for our service
foreach(ServiceController service in services) {
if (String.Compare(serviceName,service.ServiceName,true) == 0) return true;
}
// Return
return false;
}
private static void NativeImage(bool install)
{
// Generate required filenames
string service_filename = Assembly.GetExecutingAssembly().Location;
string runtime_dir = RuntimeEnvironment.GetRuntimeDirectory();
string ngen_filename = Path.Combine(runtime_dir,"ngen.exe");
// Work out arguments
string args = String.Format("{0} \"{1}\"",(install ? "install" : "uninstall"),service_filename);
// Create process
Process process = new Process();
process.StartInfo.FileName = ngen_filename;
process.StartInfo.Arguments = args;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
// Start process and wait for it to complete
process.Start();
process.WaitForExit();
}
}
}
After you have done this using the class is very straightforward, you simple call one static method, ProcessArguments and pass in the arguments supplied to the service. An example of usage can be seen below, taken directly from my service.
static void Main(string[] args)
{
// Process any arguments
if (ServiceInstaller.ProcessArguments(SERVICE_NAME,args)) return;
// Run the service
ServiceBase.Run(new Service());
}
I hope you will find this useful, I’m open to suggestions for improvements and changes so please feel free to contact me. I’ve included a direct link to the source code below to make things easier for you.
| svcinst.zip 2 KB |



