<?xml version="1.0" encoding="UTF-8"?><item><title>Installing .NET Services</title><description>&lt;DIV&gt;I've been trying to figure out how install and uninstall a .NET service.&lt;/DIV&gt;&#13;
&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&#13;
&lt;DIV&gt;There is a managed wrapper for the SCM in the form of the &lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemserviceprocessservicecontrollerclasstopic.asp"&gt;ServiceController&lt;/A&gt; class, which is very nice for doing things with installed services,&amp;nbsp;but it has no support for installing or uninstalling a service.&amp;nbsp; It doesn't even support determining whether a service is installed or not - you have to get the entire service list and iterate through it yourself.&lt;/DIV&gt;&#13;
&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&#13;
&lt;DIV&gt;The framework provides installer support in the form of the &lt;A href="http://msdn.microsoft.com/library/en-us/cpref/html/frlrfsystemconfigurationinstall.asp"&gt;System.Configuration.Install&lt;/A&gt; namespace, which has a ProjectInstaller and a&amp;nbsp;ServiceProcessInstaller.&amp;nbsp; Seems simple enough - you mark these up with the [&lt;A href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemComponentModelRunInstallerAttributeClassTopic.asp"&gt;RunInstaller&lt;/A&gt;] attribute, and use the &lt;A href="http://msdn.microsoft.com/library/en-us/cptools/html/cpconInstallerUtilityInstallutilexe.asp"&gt;InstallUtil.exe&lt;/A&gt; utility to install the service.&lt;/DIV&gt;&#13;
&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&#13;
&lt;DIV&gt;There are a few problems with this scheme.&amp;nbsp; First, this means a service isn't self-installing.&amp;nbsp; Users are used to being able to invoke &amp;#8220;servicename.exe -i&amp;#8221; to install a service.&lt;/DIV&gt;&#13;
&lt;DIV&gt;&amp;nbsp;&lt;/DIV&gt;&#13;
&lt;DIV&gt;&lt;A href="http://staff.develop.com/candera/weblog2/articleview.aspx/CLR%20Workings/Self-Installing%20Service.xml"&gt;Craig Andera&lt;/A&gt; figured out how to make a service self-installing; the code on his page doesn't quite compile; here's a version that does:&lt;/DIV&gt;&#13;
&lt;DIV&gt;&lt;FONT size=2&gt;&#13;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;&#13;
&lt;P&gt;&lt;/FONT&gt;&lt;FONT color=#008000 size=2&gt;// The main entry point for the process&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;static&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;void&lt;/FONT&gt;&lt;FONT size=2&gt; Main(&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;string&lt;/FONT&gt;&lt;FONT size=2&gt;[] args)&lt;BR&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp; if&lt;/FONT&gt;&lt;FONT size=2&gt; (args.Length &amp;gt; 0)&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; if&lt;/FONT&gt;&lt;FONT size=2&gt; (args[0] == "-i") &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TransactedInstaller ti = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; TransactedInstaller(); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProjectInstaller pi = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; ProjectInstaller(); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.Installers.Add(pi); &lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; string&lt;/FONT&gt;&lt;FONT size=2&gt; basePath = Assembly.GetExecutingAssembly ().Location;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String path = String.Format("/assemblypath=\"{0}\"", basePath); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] cmdline = {path}; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InstallContext ctx = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; InstallContext(Path.ChangeExtension(basePath, ".InstallLog"), cmdline); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.Context = ctx; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.Install (&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; Hashtable()); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;if&lt;/FONT&gt;&lt;FONT size=2&gt; (args[0] == "-u") &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TransactedInstaller ti = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; TransactedInstaller (); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProjectInstaller pi = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; ProjectInstaller (); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.Installers.Add (pi); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String path = String.Format("/assemblypath=\"{0}\"", Assembly.GetExecutingAssembly ().Location); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String[] cmdline = {path, servicename}; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InstallContext ctx = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; InstallContext(Path.ChangeExtension(basePath, ".UninstallLog"), cmdline); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.Context = ctx; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ti.Uninstall ( &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;null&lt;/FONT&gt;&lt;FONT size=2&gt; ); &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp; } &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;else&lt;BR&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.ServiceProcess.ServiceBase[] ServicesToRun;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ServicesToRun = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; System.ServiceProcess.ServiceBase[] { &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; Service1() };&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.ServiceProcess.ServiceBase.Run(ServicesToRun);&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;}&lt;/FONT&gt;&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&#13;
&lt;P&gt;&lt;FONT size=2&gt;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).&amp;nbsp; 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..&lt;/FONT&gt;&lt;/P&gt;&#13;
&lt;P&gt;&lt;FONT size=2&gt;So I ended up using some code written by &lt;A href="http://www.c-sharpcorner.com/Code/2003/Sept/InstallingWinServiceProgrammatically.asp"&gt;Sachim Nigam&lt;/A&gt; that wraps the Service Control Manager and lets you call it directly.&amp;nbsp; &lt;/FONT&gt;&lt;/P&gt;&#13;
&lt;P&gt;&lt;FONT size=2&gt;Equivalent code ends up looking like this:&lt;/FONT&gt;&lt;/P&gt;&lt;FONT size=2&gt;&#13;
&lt;BLOCKQUOTE dir=ltr style="MARGIN-RIGHT: 0px"&gt;&#13;
&lt;P&gt;&lt;/FONT&gt;&lt;FONT color=#008000 size=2&gt;// The main entry point for the process&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;static&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;void&lt;/FONT&gt;&lt;FONT size=2&gt; Main(&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;string&lt;/FONT&gt;&lt;FONT size=2&gt;[] args)&lt;BR&gt;{&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp; if&lt;/FONT&gt;&lt;FONT size=2&gt; (args.Length &amp;gt; 0)&lt;BR&gt;&amp;nbsp; {&lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;if&lt;/FONT&gt;&lt;FONT size=2&gt; (args[0] == "-i")&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; {&amp;nbsp;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SvcInstaller.ServiceInstaller si = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; SvcInstaller.ServiceInstaller();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; si.InstallService(Assembly.GetExecutingAssembly().Location, "MyService", "This is my service.");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; } &lt;BR&gt;&lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; else&lt;/FONT&gt;&lt;FONT size=2&gt; &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;if&lt;/FONT&gt;&lt;FONT size=2&gt; (args[0] == "-u") &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; SvcInstaller.ServiceInstaller si = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; SvcInstaller.ServiceInstaller();&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; si.UnInstallService("MyService");&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;BR&gt;&amp;nbsp; } &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;else&lt;BR&gt;&lt;/FONT&gt;&lt;FONT size=2&gt;&amp;nbsp; {&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.ServiceProcess.ServiceBase[] ServicesToRun;&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; ServicesToRun = &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; System.ServiceProcess.ServiceBase[] { &lt;/FONT&gt;&lt;FONT color=#0000ff size=2&gt;new&lt;/FONT&gt;&lt;FONT size=2&gt; Service1() };&lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.ServiceProcess.ServiceBase.Run(ServicesToRun);&lt;BR&gt;&amp;nbsp; }&lt;BR&gt;}&lt;/P&gt;&lt;/BLOCKQUOTE&gt;&lt;/FONT&gt;&#13;
&lt;P&gt;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.&lt;/P&gt;&#13;
&lt;P&gt;(On the same topic, having a &amp;#8220;-d&amp;#8221; 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).&lt;/P&gt;&#13;
&lt;P&gt;&lt;FONT size=2&gt;&amp;nbsp;&lt;/P&gt;&lt;/FONT&gt;&lt;/DIV&gt;</description><pubDate>Thu, 01 Sep 2005 19:17:05 GMT</pubDate></item>