Click here to Skip to main content
Licence 
First Posted 5 Jul 2007
Views 253,732
Downloads 3,375
Bookmarked 206 times

Install a Windows Service in a smart way instead of using the Windows Installer MSI package

By | 30 Jul 2007 | Article
Install a Windows Service dynamically (even with username and password) using the WindowsServiceInstallUtil C# class

Introduction

The usual way to install a Windows service is to use an MSI install file. In fact, using an MSI is only one of several ways to install a Windows service. In some particular situations, a Windows service may be needed to install and uninstall many times. Someone might even think of a way to "install it with a programming language" instead of clicking on MSI or running the InstallUtil (.NET SDK utility) command line.

This article will explain how to install a Windows service in a smart way. In addition, the installer class has been extended to support both the usual installation way with MSI or InstallUtil and the new dynamic way in which we can pass in a username and password -- from a DB, for instance -- to start a Windows service with that specific user.

Background

I came up with the idea to deliver this solution when we made an automated integration testing system in which there are executables and Windows services that have to be started before doing testing. The process of installing, uninstalling, starting and stopping executables and Windows services must be automatic. The only way to do that is for our testing system to be able to install and uninstall Windows services instead of using MSI or installutil.

I tried to look for any available C# or even VB.NET coding class from the Internet that could help me solve this problem, but that was useless. Luckily, I found an interesting article submitted by gtamir. What he did was actually just change the installer class and create a BAT file, replacing the installutil command line. However, the idea of extending arguments of installutil is truly great and I was able to create an advanced C# installutil class from there.

DynamicInstaller

The .NET SDK InstallUtil.exe utility invokes the ProjectInstaller module in your assembly. For installation, InstallUtil monitors all installation steps and rolls back the installation if an error occurs. For uninstalling, InstallUtil runs the Uninstall code in your ProjectInstaller module. For more information about the install process, see MSDN documentation for the ServiceInstaller class.

Screenshot - InstallUtil.gif

using System;
using System.Collections;
using System.Configuration.Install;
using System.ServiceProcess;
using System.ComponentModel;

[RunInstallerAttribute(true)]
public class ProjectInstaller : Installer{
    private ServiceInstaller serviceInstaller1;
    private ServiceInstaller serviceInstaller2;
    private ServiceProcessInstaller processInstaller;

    public MyProjectInstaller(){
        // Instantiate installers for process and services.
        processInstaller = new ServiceProcessInstaller();
        serviceInstaller1 = new ServiceInstaller();
        serviceInstaller2 = new ServiceInstaller();

        // The services run under the system account.
        processInstaller.Account = ServiceAccount.LocalSystem;

        // The services are started manually.
        serviceInstaller1.StartType = ServiceStartMode.Manual;
        serviceInstaller2.StartType = ServiceStartMode.Manual;

        // ServiceName must equal those on ServiceBase derived classes.
        serviceInstaller1.ServiceName = "Hello-World Service 1";
        serviceInstaller2.ServiceName = "Hello-World Service 2";

        // Add installers to collection. Order is not important.
        Installers.Add(serviceInstaller1);
        Installers.Add(serviceInstaller2);
        Installers.Add(processInstaller);
    }
}

Since the InstallUtil command line doesn't support any arguments containing accountType, userName or password when installing a Windows service, all settings such as serviceName, accountType, userName, password, etc. will be taken from the Installer inside the Windows service assembly. What can you do if you want to install a Windows service to start under a user account? When using the default Installer, there are only two ways to do that: type in the username and password box during installation or have a fixed username and password inside the Installer code.

Screenshot - SetServiceLogin.gif

Therefore DynamicInstaller was created in order to pass in the username and password dynamically from anywhere you want, e.g. a database. The beautiful thing is that it still keeps the original features of the default Installer so that if you want to use MSI or InstallUtil as usual, that's also possible and even much easier with the properties created.

Screenshot - DynamicInstallerProperties.gif

Because DynamicInstaller is stored inside a separated assembly, ServiceInstaller.dll, it's easy to be inherited by a different ProjectInstaller on different projects.

using System;
using System.Collections;
using System.ComponentModel;
using System.Configuration.Install;
using System.ServiceProcess;
using Microsoft.Win32;
namespace ServiceInstaller
{
    /// <summary>
    /// This is a custom project installer.
    /// Applies a unique name to the service using the /name switch
    /// Sets user name and password using the /user and /password switches
    /// Allows the use of a local account using the /account switch
    /// </summary>
    [RunInstaller(true)]
    public class DynamicInstaller : Installer
    {
        public string ServiceName
        {
            get { return serviceInstaller.ServiceName; }
            set { serviceInstaller.ServiceName = value; }
        }
        public string DisplayName
        {
            get { return serviceInstaller.DisplayName; }
            set { serviceInstaller.DisplayName = value; }
        }
        public string Description
        {
            get { return serviceInstaller.Description; }
            set { serviceInstaller.Description = value; }
        }
        public ServiceStartMode StartType
        {
            get { return serviceInstaller.StartType; }
            set { serviceInstaller.StartType = value; }
        }
        public ServiceAccount Account
        {
            get { return processInstaller.Account; }
            set { processInstaller.Account = value; }
        }
        public string ServiceUsername
        {
            get { return processInstaller.Username; }
            set { processInstaller.Username = value; }
        }
        public string ServicePassword
        {
            get { return processInstaller.Password; }
            set { processInstaller.Password = value; }
        }
        private ServiceProcessInstaller processInstaller;
        private System.ServiceProcess.ServiceInstaller serviceInstaller;
        public DynamicInstaller()
        {
            processInstaller = new ServiceProcessInstaller();
            processInstaller.Account = ServiceAccount.LocalService;
            processInstaller.Username = null;
            processInstaller.Password = null;
            serviceInstaller = new System.ServiceProcess.ServiceInstaller();
            serviceInstaller.StartType = ServiceStartMode.Automatic;
            serviceInstaller.ServiceName = "MyService";
            serviceInstaller.DisplayName = "";
            serviceInstaller.Description = "";
            Installers.AddRange(new Installer[] {
            processInstaller,
            serviceInstaller});
        }
        #region Access parameters
        /// <summary>
        /// Return the value of the parameter in dictated by key
        /// </summary>
        /// <PARAM name="key">Context parameter key</PARAM>
        /// <returns>Context parameter specified by key</returns>
        public string GetContextParameter(string key)
        {
            string sValue = "";
            try
            {
                sValue = this.Context.Parameters[key].ToString();
            }
            catch
            {
                sValue = "";
            }
            return sValue;
        }
        #endregion
        /// <summary>
        /// This method is run before the install process.
        /// This method is overridden to set the following parameters:
        /// service name (/name switch)
        /// account type (/account switch)
        /// for a user account user name (/user switch)
        /// for a user account password (/password switch)
        /// Note that when using a user account,
        /// if the user name or password is not set,
        /// the installing user is prompted for the credentials to use.
        /// </summary>
        /// <PARAM name="savedState"></PARAM>
        protected override void OnBeforeInstall(IDictionary savedState)
        {
            base.OnBeforeInstall(savedState);
            bool isUserAccount = false;

            // Decode the command line switches
            string name = GetContextParameter("name").Trim();
            if (name != "")
            {
                serviceInstaller.ServiceName = name;
            }
            string desc = GetContextParameter("desc").Trim();
            if (desc != "")
            {
                serviceInstaller.Description = desc;
            }
            // What type of credentials to use to run the service
            string acct = GetContextParameter("account");
            switch (acct.ToLower())
            {
                case "user":
                    processInstaller.Account = ServiceAccount.User;
                    isUserAccount = true;
                    break;
                case "localservice":
                    processInstaller.Account = ServiceAccount.LocalService;
                    break;
                case "localsystem":
                    processInstaller.Account = ServiceAccount.LocalSystem;
                    break;
                case "networkservice":
                    processInstaller.Account = ServiceAccount.NetworkService;
                    break;
            }
            // User name and password
            string username = GetContextParameter("user").Trim();
            string password = GetContextParameter("password").Trim();
            // Should I use a user account?
            if (isUserAccount)
            {
                // If we need to use a user account,
                // set the user name and password
                if (username != "")
                {
                    processInstaller.Username = username;
                }
                if (password != "")
                {
                    processInstaller.Password = password;
                }
            }
        }
        /// <summary>
        /// Uninstall based on the service name
        /// </summary>
        /// <PARAM name="savedState"></PARAM>
        protected override void OnBeforeUninstall(IDictionary savedState)
        {
            base.OnBeforeUninstall(savedState);
            // Set the service name based on the command line
            string name = GetContextParameter("name").Trim();
            if (name != "")
            {
                serviceInstaller.ServiceName = name;
            }
        }
    }//end class
}

Now it becomes easy to install a Windows service with a programming language by using WindowsServiceInstallUtil.cs. This class contains many options in constructors for convenient usage. The fully customized way to install a Windows service is by passing in every piece of information, such as serviceName, description, assemblyLocation, accountType, username, password, etc.

//
// Installing windows service to start with username and password
//
//If install with local user:
//wsInstallInfo = new WindowsServiceInstallInfo
    ("MyService", "desc", Directory.GetCurrentDirectory(), "MyService.exe",
// WindowsServiceAccountType.User, @".\username", @"password");
//If install with network user:
//wsInstallInfo = new WindowsServiceInstallInfo
    ("MyService", "desc", Directory.GetCurrentDirectory(), "MyService.exe",
// WindowsServiceAccountType.User, @"networkdomain\username", @"password");
wsInstallInfo = new WindowsServiceInstallInfo
    ("MyService", "desc", Directory.GetCurrentDirectory(), "MyService.exe",
WindowsServiceAccountType.User, @".\username", @"");
wsInstallUtil = new WindowsServiceInstallUtil(wsInstallInfo);
//Log to see any error
//wsInstallUtil.Install(@"C:\test.txt");
result = wsInstallUtil.Install();
Console.WriteLine("Installed : " + result);
Console.WriteLine("Press any key to continue ...");
Console.ReadKey();
//Log to see any error
//wsInstallUtil.Uninstall(@"C:\test.txt");
result = wsInstallUtil.Uninstall();
Console.WriteLine("Uninstalled : " + result);
Console.WriteLine("Press any key to continue ...");
Console.ReadKey();

Screenshot - MyService.gif

Points of interest

Please bear in mind that you can still install this Windows service assembly via MSI as usual. You can even create a *.bat file to execute the InstallUtil command instead of using the C# installutil class. This is installService.bat:

@echo off
set SERVICE_HOME=<service executable directory>
set SERVICE_EXE=<service executable name>
REM the following directory is for .NET 1.1, your mileage may vary
set INSTALL_UTIL_HOME=C:\WINNT\Microsoft.NET\Framework\v1.1.4322
REM Account credentials if the service uses a user account
set USER_NAME=<user account>
set PASSWORD=<user password>

set PATH=%PATH%;%INSTALL_UTIL_HOME%

cd %SERVICE_HOME%

echo Installing Service...
installutil /name=<service name>
  /account=<account type> /user=%USER_NAME% /password=%

PASSWORD% %SERVICE_EXE%

echo Done.

The variables set at the top are for convenience only. You can certainly hardcode all information directly on the installutil line. This is uninstallService.bat:

@echo off
set SERVICE_HOME=<service executable directory>
set SERVICE_EXE=<service executable name>
REM the following directory is for .NET 1.1, your mileage may vary
set INSTALL_UTIL_HOME=C:\WINNT\Microsoft.NET\Framework\v1.1.4322

set PATH=%PATH%;%INSTALL_UTIL_HOME%

cd %SERVICE_HOME%

echo Uninstalling Service...
installutil /u /name=<service name> %SERVICE_EXE%

echo Done.

History

  • 5 July, 2007 -- Original version posted
  • 7 July, 2007 -- Article moved
  • 30 July, 2007 -- Downloads updated

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

nguyenthanhtungtinbk

Team Leader

Vietnam Vietnam

Member


PERSONAL DETAILS


Full Name : Nguyen Thanh Tung
Sex : Male
Date of Birth : October 06, 1983
Nationality : Vietnamese

PERSONAL PROFILE

Trustworthy, efficient, capable work under pressure, outgoing person and capable work in a team.

 

AREA OF EXPERTISE

    • Proficient in Programming: .Net 1.0/1.1/2.0/3.0/3.5 (C#, VB.Net, ASP.Net,
      AJAX, WCF, WWF), VB4/5/6, ASP, PHP, HTML, CSS, Script, XML.
      Experience with Vs Source Safe, Team System, MsBuild, SVN/CVS. Knowledge about
      UnitTest, Test-Driven development.
    • Experience with Databases: SQL Server 2000/2005, MySQL
      4.x/5.x/6.x, Oracle, PostgreSQL, Ms. Access.
    • Experience in EIS: ERP, CRM, SCM ...
    • Experience with Microsoft Products: CRM3/4, Sharepoint
      2003/2007 ...
    • Proficient in OS: Linux (Redhat, Fedora, Mandrake, Suse), Windows (2000/2003
      Server, XP, Vista).
    • Proficient in Graphics (Flash, Photoshop, 3DMax …); Microsoft Office, Visio,
      Microsoft Project.
    • Proficient in Network Devices: Router, Switch, Firewall Server, Mail Server,
      FTP Server, DNS, VPN.
      Experience in Servers: IBM, HP, Sun, Dell ...
    • Familiar with OO and SOA concepts.
    • Knowledge about project management: CMMI, Agile and Scrum, Software
      Engineering, System Analysis and Design, IT Project Management. 
    • Knowledge in Accounting and Finance: Financial Statement, Income
      Statement ...
     


    MAIN PROJECTS DONE IN THE PAST
    • SMS Gateway.
    • POP3-IMAP-SMTP App.
    • Products Management Sys.
    • Hedge Fund Management Sys.
    • Motion detection.
    • 3D games engine (C# using OpenGL Lib).
    • Video Conference.
    • Information Retrieval.
    • Microsoft CRM3/4
    • Microsoft Sharepoint Portal 2003/2007
    • CMS
    • E-Commerce



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionRe The installer PinmemberRashmi Sasikumar7:25 3 May '12  
GeneralMy vote of 4 Pinmemberkhaledelshiekh17:32 14 Jan '12  
Generalerrors and exceptions Pinmemberjmostrosky6:33 11 Aug '10  
GeneralUser install Pinmemberhammet19822:52 16 Nov '09  
Questionis the SMS Gateway you did is open source ? PinmemberMuneer Safi4:37 13 Apr '09  
QuestionHow to add service parameters PinmemberBvu#0073:52 16 Feb '09  
AnswerRe: How to add service parameters PinmemberKikoz686:20 26 Jan '10  
GeneralDynamicInsaller in another assembly PinmemberWerner Cloete5:00 12 Jan '09  
QuestionWhy didn't you just invoke the ManagedInstallerClass? Pinmemberkhazzard10:56 22 Nov '07  
GeneralService name doesn't change Pinmemberjonashilmersson23:19 28 Oct '07  
GeneralRe: Service name doesn't change Pinmemberjonashilmersson3:25 29 Oct '07  
GeneralRe: Service name doesn't change Pinmemberjonashilmersson4:29 29 Oct '07  
Generalthe logfile Pinmemberaovidiu0:37 5 Oct '07  
GeneralRe: the logfile Pinmembernguyenthanhtungtinbk7:58 5 Oct '07  
Questionsame article? Pinmemberthompsonson24:55 12 Sep '07  
AnswerRe: same article? Pinmembernguyenthanhtungtinbk7:44 12 Sep '07  
GeneralAvoid InstallUtil and .msi when installing Windows Services PinmemberKennexis3:04 11 Aug '07  
GeneralRe: Avoid InstallUtil and .msi when installing Windows Services Pinmembernguyenthanhtungtinbk11:35 11 Aug '07  
GeneralSomething strange PinmemberStyxke3:42 9 Aug '07  
GeneralRe: Something strange Pinmembernguyenthanhtungtinbk20:42 9 Aug '07  
GeneralRe: Something strange PinmemberStyxke21:24 12 Aug '07  
Thanks!
I was doing something wrong ... it works perfectly now.
I did make an addition though, I added the ServicesDependedOn property, because I need this one, so if you're interested in the addition I'll send it to you.
GeneralA bit of magic PinmemberDarov2:51 3 Aug '07  
GeneralRe: A bit of magic Pinmembernguyenthanhtungtinbk8:17 3 Aug '07  
GeneralRe: A bit of magic PinmemberDarov12:52 3 Aug '07  
GeneralUn/Installed : False PinmemberDynV11:00 1 Aug '07  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web01 | 2.5.120529.1 | Last Updated 30 Jul 2007
Article Copyright 2007 by nguyenthanhtungtinbk
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid