Introduction
Using the InstallUtil.exe utility that ships with the .NET SDK can be a real pain. It's rarely in the PATH so you probably have to hunt down the utility when you are working on QA and production servers as I do. Installing a Windows Service should be easier. In this short article, I'll show you a way to make your Windows Services install themselves without needing InstallUtil.exe at all.
Assumptions
Let's assume that your service project has a service installer, a service process installer and a class derived from
System.Configuration.Install.Installer
already. If not, check out Mahmoud Nasr's excellent article on Windows Service development, then come back here.Key Information
Thanks to Reflector for .NET by Lutz Roeder, it's easy to discover how the InstallUtil.exe utility does its job. After some setup, the InstallUtil.exe tool jumps to a method called
InstallHelper
in the ManagedInstallerClass
in theSystem.Configuration.Install
namespace. And what's really interesting is that the command line arguments passed to InstallUtil.exe as an array of strings are then passed directly to this helper method.
Well, this made me think, "If all InstallUtil.exe does is call the
ManagedInstallerClass
' InstallHelper
method, why can't my service executable do the same thing to install itself on command?" The little class below makes it simple to do just that.Using the Code
Create a new CS file in your service executable project containing the following code. You may also need to add a reference to the System.Configuration.Install.dll from the Global Assembly Cache if you don't already have one.
using System.Reflection;
using System.Configuration.Install;
namespace gotnet.biz.Utilities.WindowsServices
{
public static class SelfInstaller
{
private static readonly string _exePath =
Assembly.GetExecutingAssembly().Location;
public static bool InstallMe()
{
try
{
ManagedInstallerClass.InstallHelper(
new string[] { _exePath } );
}
catch
{
return false;
}
return true;
}
public static bool UninstallMe()
{
try
{
ManagedInstallerClass.InstallHelper(
new string[] { "/u", _exePath } );
}
catch
{
return false;
}
return true;
}
}
}
Now you need to come up with some sort of convention for knowing when to invoke the installer. Below is just an example of how you might handle this in your
Main()
method, the entry point to your service. I like the convention of using -i
or -install
parameters to install the service and -u
or -uninstall
to uninstall it. I also like to use -c
or -console
to mean starting the application in a console rather than as a service. However, that's a topic for a different article.using gotnet.biz.Utilities.WindowsServices;
namespace MyService.WinHost
{
static class Program
{
public static void Main( string[] args )
{
if (args != null && args.Length == 1 && args[0].Length > 1
&& (args[0][0] == '-' || args[0][0] == '/'))
{
switch (args[0].Substring( 1 ).ToLower())
{
default:
break;
case "install":
case "i":
SelfInstaller.InstallMe();
break;
case "uninstall":
case "u":
SelfInstaller.UninstallMe();
break;
case "console":
case "c":
MyConsoleHost.Launch();
break;
}
}
else
MyWinServiceHost.Launch();
}
}
}
Now, assuming my executable is named MyWinSvcHost.exe, I can invoke the installer by running:
C:\> MyWinSvcHost.exe -install
Or to uninstall my service, I would use this:
C:\> MyWinSvcHost.exe -uninstall
Other Ideas
This little bit of code called the
SelfInstaller
is full of possibilities. You could pass parameters to the InstallMe
method to pass on to the ServiceProcessInstaller
in your program, for example. Perhaps the domain name, user name and password used to start your service could be passed all the way from your Main()
method to theServiceProcessInstaller
. Cool, right? I thought you would like that.
No comments:
Post a Comment