Simple Installer for Windows Service using Visual Studio 2012

Many developers seem to want the GUI windows service install approach.  It appears as if Microsoft removed something from at least some of the SKU’s for Visual Studio 2012, as there is a stackoverflow question mentioning the ‘Add Installer’ menu item doesn’t work for them.

I prefer the simple command line approach to windows service installs, but without typing the commands manually every time.  You don’t need any special tooling to create an install path for a windows service.  All the tooling does is create a small and simple class.  All you need is a batch file with a command line to run the install and another to run an uninstall.

Creating a ProjectInstaller Class

If you have the tooling, just right click on the service design surface and select “Add Installer”.  If the tooling is not setup properly you may get this error:

Unable to add installer. The designer could not add a Project Installer.

All the tooling does is create this simple partial class (2 files).

ProjectInstaller.Designer.cs

namespace Candor.Tasks.MultiWorkerService
{
    partial class ProjectInstaller
    {
        /// <summary>
        /// Required designer variable.
        /// </summary>
        private System.ComponentModel.IContainer components = null;

        /// <summary> 
        /// Clean up any resources being used.
        /// </summary>
        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing &amp;amp;&amp;amp; (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        #region Component Designer generated code

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
            this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller();
            this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller();
            // 
            // serviceProcessInstaller1
            // 
            this.serviceProcessInstaller1.Password = null;
            this.serviceProcessInstaller1.Username = null;
            // 
            // serviceInstaller1
            // 
            this.serviceInstaller1.ServiceName = "CandorWorkerRoleService";
            // 
            // ProjectInstaller
            // 
            this.Installers.AddRange(new System.Configuration.Install.Installer[] {
            this.serviceProcessInstaller1,
            this.serviceInstaller1});

        }

        #endregion

        private System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1;
        private System.ServiceProcess.ServiceInstaller serviceInstaller1;
    }
}

ProjectInstaller.cs

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration.Install;
using System.Linq;
using System.Threading.Tasks;

namespace Candor.Tasks.MultiWorkerService
{
    [RunInstaller(true)]
    public partial class ProjectInstaller : System.Configuration.Install.Installer
    {
        public ProjectInstaller()
        {
            InitializeComponent();
        }
    }
}

If the tooling does not work for you, then just create those two classes manually. Just update the namespaces, and the ServiceName value appropriately for your project.

Modifying the installer

If you have the tooling in place, then just follow the instructions at arcane code for using the designer surface to rename a few items and add a description.  Or follow along here and do it via code.

In the designer file code behind, change the service account used to run your windows service to the desired account type or credentials.  I changed mine to run as LocalSystem per the following one line change.

            // 
            // serviceProcessInstaller1
            // 
            this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.LocalSystem;
            this.serviceProcessInstaller1.Password = null;
            this.serviceProcessInstaller1.Username = null;

Then change the service details that will be listed in the service manager after installation.

            // 
            // serviceInstaller1
            // 
            this.serviceInstaller1.Description = "Performs various configured tasks";
            this.serviceInstaller1.DisplayName = "Candor Worker";
            this.serviceInstaller1.ServiceName = "CandorWorkerRoleService";
            this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic;

I changed the display name and description for the benefit of any future server admin.  I also changed the startup type to automatic so that the service starts anytime the server restarts.  Change these to whatever suits your needs.

Creating an install / uninstall batch command

To install this service on the server you only need to run a simple command at the command prompt, as administrator.  But to make frequent installs simple, I usually put these into batch files.  I include these files in the windows service project.



_installer.bat

This command line executes InstallUtil which is part of windows server operating systems. Just change the name of the exe after the /i switch to be the output file name of your windows service project.

@ECHO OFF

echo Installing WindowsService...
echo ---------------------------------------------------
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil /i Candor.Tasks.MultiWorkerService.exe
echo ---------------------------------------------------
echo Done.
pause

_uninstaller.bat

This command line executes InstallUtil which is part of windows server operating systems. Just change the name of the exe after the /u switch to be the output file name of your windows service project.

@ECHO OFF

echo Un-Installing WindowsService...
echo ---------------------------------------------------
C:\WINDOWS\Microsoft.NET\Framework\v4.0.30319\InstallUtil /u Candor.Tasks.MultiWorkerService.exe
echo ---------------------------------------------------
echo Done.
pause

Installing your service

Do not use the publish command on the project node.  The following directions have worked since at least Visual Studio 2005 and continue to work today in Visual Studio 2012.  Just switch to release configuration and build the project.  Then navigate to the build output folder and copy the contents to a staging folder on your server.  Make sure to include the _install.bat and _uninstall.bat.

If a prior version of the service was installed follow the uninstall directions before continuing.

I also recommend creating a folder to archive each release output before installation just in case configuration files were manually modified since the last deployment, and backing up the files before overwriting the previous installation, just in case you need to rollback sometime.  After backing up and uninstalling the prior version of your service, if applicable, then overwrite the target install folder contents with your latest version.

You need to run the _install.bat file, but you can’t do it by double clicking on it, or even selecting “run as administrator” in the right click menu.  If you do then you’ll get an error:

Exception occurred while initializing the installation:
System.IO.FileNotFoundException: Could not load file or assembly ‘file:///C:\Windows\system32\Candor.Tasks.MultiWorkerService.exe’ or one of its dependencies. The system cannot find the file specified..

Open a command prompt as administrator, navigate to the install folder, and then run _install.bat.  Your output should look something like the following.

InstallWindowsService_InstallConsoleOutput_PreviewC:\Windows\system32>cd C:\Users\micha_000\Documents\GitHub\candor-common\Candor.
Tasks.MultiWorkerService\bin\Release

C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks.MultiWorkerServic
e\bin\Release>_installer.bat
Installing WindowsService…
—————————————————
Microsoft (R) .NET Framework Installation utility Version 4.0.30319.18010
Copyright (C) Microsoft Corporation. All rights reserved.
Running a transacted installation.

Beginning the Install phase of the installation.
See the contents of the log file for the C:\Users\micha_000\Documents\GitHub\can
dor-common\Candor.Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerS
ervice.exe assembly’s progress.
The file is located at C:\Users\micha_000\Documents\GitHub\candor-common\Candor.
Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.InstallLog.

Installing assembly ‘C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Ta
sks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.exe’.
Affected parameters are:
i =
logfile = C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks.Mult
iWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.InstallLog
assemblypath = C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks
.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.exe
logtoconsole =
Installing service CandorWorkerRoleService…
Service CandorWorkerRoleService has been successfully installed.
Creating EventLog source CandorWorkerRoleService in log Application…

The Install phase completed successfully, and the Commit phase is beginning.
See the contents of the log file for the C:\Users\micha_000\Documents\GitHub\can
dor-common\Candor.Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerS
ervice.exe assembly’s progress.
The file is located at C:\Users\micha_000\Documents\GitHub\candor-common\Candor.
Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.InstallLog.

Committing assembly ‘C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Ta
sks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.exe’.
Affected parameters are:
i =
logfile = C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks.Mult
iWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.InstallLog
assemblypath = C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks
.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.exe
logtoconsole =

The Commit phase completed successfully.

The transacted install has completed.
—————————————————
Done.
Press any key to continue . . .

Then open the computer management services node and start your newly installed service.  If you need to run as a specific user, then setting that credential should be done before starting the service.  I don’t set the run-time credentials in the ProjectInstaller class in the service for security reasons, and instead default to LocalSystem.  Then if my service code needs elevated permissions for any reason, then I handle setting those credentials at install time.

Incorrect format error

A very common error with mixed 32bit developer workstation and 64 bit server operating systems, you may get this error.

System.BadImageFormatException: Could not load file or assembly [fileName] or one of its dependencies. An attempt was made to load a program with an incorrect format.

If this happens, then you compiled as the wrong platform target for your server install.  Update your project properties ‘platform target’ to compile to the same bit size as your server OS bit size or as “Any CPU”.  If one of your references is to an assembly of the wrong bit size the same error will occur; So check all your references also.

InstallWindowsService_ProjectBuildProps

Uninstalling your service

The uninstall process is similar to install.  First stop your service in the services section of computer management.  Then run the _uninstall.bat file, but you can’t do it by double clicking on it, or even selecting “run as administrator” in the right click menu.  If you do then you’ll get an error:

Exception occurred while initializing the installation:
System.IO.FileNotFoundException: Could not load file or assembly ‘file:///C:\Windows\system32\Candor.Tasks.MultiWorkerService.exe’ or one of its dependencies. The system cannot find the file specified..

Instead open a command prompt as administrator, navigate to the install folder, and then run _uninstall.bat.  Your output should look something like the following.

C:\Windows\system32>cd C:\Users\micha_000\Documents\GitHub\candor-common\Candor.
Tasks.MultiWorkerService\bin\Release

C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks.MultiWorkerServic
e\bin\Release>_uninstaller.bat
Un-Installing WindowsService…
—————————————————
Microsoft (R) .NET Framework Installation utility Version 4.0.30319.18010
Copyright (C) Microsoft Corporation. All rights reserved.

The uninstall is beginning.
See the contents of the log file for the C:\Users\micha_000\Documents\GitHub\can
dor-common\Candor.Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerS
ervice.exe assembly’s progress.
The file is located at C:\Users\micha_000\Documents\GitHub\candor-common\Candor.
Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.InstallLog.

Uninstalling assembly ‘C:\Users\micha_000\Documents\GitHub\candor-common\Candor.
Tasks.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.exe’.
Affected parameters are:
logtoconsole =
logfile = C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks.Mult
iWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.InstallLog
assemblypath = C:\Users\micha_000\Documents\GitHub\candor-common\Candor.Tasks
.MultiWorkerService\bin\Release\Candor.Tasks.MultiWorkerService.exe
Removing EventLog source CandorWorkerRoleService.
Service CandorWorkerRoleService is being removed from the system…
Service CandorWorkerRoleService was successfully removed from the system.

The uninstall has completed.
—————————————————
Done.
Press any key to continue . . .

References

How to create a setup project for a Windows Service application in Visual C# (VS 2003-2008)
http://support.microsoft.com/kb/816169

Windows Services in C#: Adding the Installer (part 3)
http://arcanecode.com/2007/05/23/windows-services-in-c-adding-the-installer-part-3/

Debugging a Windows Service without Installation
http://candordeveloper.com/2012/12/27/debugging-a-windows-service-application-without-install/

Development Specs

The instructions here work on my machine with the following specs.  Your mileage may vary.

Dell Precision M4300 (very old, built in 2008)
Windows 8 Pro N, Version 6.2.9200 Build 9200
Microsoft Visual Studio Professional 2012, Version 11.0.51106.01 Update 1

About the Author

Michael Lang

Co-Founder and CTO of Watchdog Creative, business development, technology vision, and more since 2013, Developer, and Mentor since 1999. See the about page for more details.

13 Comments

  • Avatar By Salih Akkaya

    thank you!

  • Avatar By Scott

    I know this is old, but the blog still comes up early on google, so i thought i’d add this.
    Make sure your files live on the machine running service. I do all my dev on a mapped/shared network drive, and it allows you to install the service, but then it won’t run..

    • mlang74 By mlang74

      Good point. I hadn’t tried installing on a file share.

  • Avatar By luis eduardo

    Very usefull post. It’s really simple.
    I just wanted to thank you for sharing and say that I added a few lines to set the current install dir. I tried it in my dev machine and worked fine. After that I only right click the file and open it as Administrator.

    @ECHO OFF

    echo Installing WindowsService…
    echo —————————————————
    REM Moving to install dir
    @set location=%~dp0
    cd /d “%location%”
    C:WINDOWSMicrosoft.NETFrameworkv4.0.30319InstallUtil /i Candor.Tasks.MultiWorkerService.exe
    echo —————————————————
    echo Done.
    pause

  • Avatar By aknutman

    Thanks, it help me a lot..

    Need permition to reshare

    • mlang74 By mlang74

      you may summarize and lead back to this blog.

  • Avatar By aknutman

    Reblogged this on Aknutman's Weblog and commented:
    Noted

  • Avatar By frankfajardo

    I know this is old, but you can also use your project .exe to perform the install and uninstall. See this SO item: http://stackoverflow.com/questions/1449994/inno-setup-for-windows-service/1450051#1450051

  • Avatar By Giles Middleton

    In VS2015 (and probably prior to this) you can install the service by just adding the primary assembly output of the service to all nodes of the all the Custom Action sequences in the .vdproj. No need for batch files.

    You can also start the service on startup by overriding the following method (no need to add an event member, it’s a plain override)

    protected override void OnAfterInstall(IDictionary savedState)
    {
    try
    {
    using (ServiceController sc = new ServiceController(this.serviceInstaller.ServiceName))
    {
    sc.Start();
    }
    }
    catch (Exception e)
    {
    System.Diagnostics.Trace.WriteLine(e,this.GetType().ToString());
    }
    }

    • Avatar By Giles Middleton

      “on startup” I meant “on install”

  • Avatar By Giles Middleton

    OOps – I should also call the baseclass OnAfterInstall according to the documentation. https://msdn.microsoft.com/en-us/library/system.configuration.install.installer.onafterinstall(v=vs.100).aspx