Installing .NET Services
// The main entry point for the process
static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[0] == "-i")
{
TransactedInstaller ti = new TransactedInstaller();
ProjectInstaller pi = new ProjectInstaller();
ti.Installers.Add(pi);
string basePath = Assembly.GetExecutingAssembly ().Location;
String path = String.Format("/assemblypath=\"{0}\"", basePath);
String[] cmdline = {path};
InstallContext ctx = new InstallContext(Path.ChangeExtension(basePath, ".InstallLog"), cmdline);
ti.Context = ctx;
ti.Install (new Hashtable());
}
else if (args[0] == "-u")
{
TransactedInstaller ti = new TransactedInstaller ();
ProjectInstaller pi = new ProjectInstaller ();
ti.Installers.Add (pi);
String path = String.Format("/assemblypath=\"{0}\"", Assembly.GetExecutingAssembly ().Location);
String[] cmdline = {path, servicename};
InstallContext ctx = new InstallContext(Path.ChangeExtension(basePath, ".UninstallLog"), cmdline);
ti.Context = ctx;
ti.Uninstall ( null );
}
} else
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Service1() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
}
However, to me, it seems there's a lot of overhead here for a fairly simple operation (especially considering that there's also a separate file containing the ProjectInstaller and it's ServiceProcessInstaller). Also, if you need to pass install-time parameters to your service, such as specifying it's service name (a fairly common requirement), then you have to pass this to the ProjectInstaller and have it update all the various installer classes that you need for installing event logs and whatnot..
So I ended up using some code written by Sachim Nigam that wraps the Service Control Manager and lets you call it directly.
Equivalent code ends up looking like this:
// The main entry point for the process
static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[0] == "-i")
{
SvcInstaller.ServiceInstaller si = new SvcInstaller.ServiceInstaller();
si.InstallService(Assembly.GetExecutingAssembly().Location, "MyService", "This is my service.");
}
else if (args[0] == "-u")
{
SvcInstaller.ServiceInstaller si = new SvcInstaller.ServiceInstaller();
si.UnInstallService("MyService");
}
} else
{
System.ServiceProcess.ServiceBase[] ServicesToRun;
ServicesToRun = new System.ServiceProcess.ServiceBase[] { new Service1() };
System.ServiceProcess.ServiceBase.Run(ServicesToRun);
}
}
It doesn't let you do anything that you can't do with the .NET Installer mechanism, but it's a lot simpler, faster, and more understandable.
(On the same topic, having a “-d” option which invokes Service1 and directly calls the OnStart method instead of calling ServiceBase.Run makes it easy to debug your service by specifying -d on the command line and just stepping into it).