How do I create an installer for a Windows Service that I have created using Visual Studio?
This question is related to
c#
.net
visual-studio
windows-services
windows-installer
I follow Kelsey's first set of steps to add the installer classes to my service project, but instead of creating an MSI or setup.exe installer I make the service self installing/uninstalling. Here's a bit of sample code from one of my services you can use as a starting point.
public static int Main(string[] args)
{
if (System.Environment.UserInteractive)
{
// we only care about the first two characters
string arg = args[0].ToLowerInvariant().Substring(0, 2);
switch (arg)
{
case "/i": // install
return InstallService();
case "/u": // uninstall
return UninstallService();
default: // unknown option
Console.WriteLine("Argument not recognized: {0}", args[0]);
Console.WriteLine(string.Empty);
DisplayUsage();
return 1;
}
}
else
{
// run as a standard service as we weren't started by a user
ServiceBase.Run(new CSMessageQueueService());
}
return 0;
}
private static int InstallService()
{
var service = new MyService();
try
{
// perform specific install steps for our queue service.
service.InstallService();
// install the service with the Windows Service Control Manager (SCM)
ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location });
}
catch (Exception ex)
{
if (ex.InnerException != null && ex.InnerException.GetType() == typeof(Win32Exception))
{
Win32Exception wex = (Win32Exception)ex.InnerException;
Console.WriteLine("Error(0x{0:X}): Service already installed!", wex.ErrorCode);
return wex.ErrorCode;
}
else
{
Console.WriteLine(ex.ToString());
return -1;
}
}
return 0;
}
private static int UninstallService()
{
var service = new MyQueueService();
try
{
// perform specific uninstall steps for our queue service
service.UninstallService();
// uninstall the service from the Windows Service Control Manager (SCM)
ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location });
}
catch (Exception ex)
{
if (ex.InnerException.GetType() == typeof(Win32Exception))
{
Win32Exception wex = (Win32Exception)ex.InnerException;
Console.WriteLine("Error(0x{0:X}): Service not installed!", wex.ErrorCode);
return wex.ErrorCode;
}
else
{
Console.WriteLine(ex.ToString());
return -1;
}
}
return 0;
}
For VS2017 you will need to add the "Microsoft Visual Studio 2017 Installer Projects" VS extension. This will give you additional Visual Studio Installer project templates. https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.MicrosoftVisualStudio2017InstallerProjects#overview
To install the windows service you can add a new setup wizard type project and follow the steps from Kelsey's answer https://stackoverflow.com/a/9021107/1040040
Nor Kelsey, nor Brendan solutions does not works for me in Visual Studio 2015 Community.
Here is my brief steps how to create service with installer:
->
New->
ProjectDouble click serviceInstaller1. Visual Studio creates serviceInstaller1_AfterInstall
event. Write code:
private void serviceInstaller1_AfterInstall(object sender, InstallEventArgs e)
{
using (System.ServiceProcess.ServiceController sc = new
System.ServiceProcess.ServiceController(serviceInstaller1.ServiceName))
{
sc.Start();
}
}
Build solution. Right click on project and select 'Open Folder in File Explorer'. Go to bin\Debug.
Create install.bat with below script:
:::::::::::::::::::::::::::::::::::::::::
:: Automatically check & get admin rights
:::::::::::::::::::::::::::::::::::::::::
@echo off
CLS
ECHO.
ECHO =============================
ECHO Running Admin shell
ECHO =============================
:checkPrivileges
NET FILE 1>NUL 2>NUL
if '%errorlevel%' == '0' ( goto gotPrivileges ) else ( goto getPrivileges )
:getPrivileges
if '%1'=='ELEV' (shift & goto gotPrivileges)
ECHO.
ECHO **************************************
ECHO Invoking UAC for Privilege Escalation
ECHO **************************************
setlocal DisableDelayedExpansion
set "batchPath=%~0"
setlocal EnableDelayedExpansion
ECHO Set UAC = CreateObject^("Shell.Application"^) > "%temp%\OEgetPrivileges.vbs"
ECHO UAC.ShellExecute "!batchPath!", "ELEV", "", "runas", 1 >> "%temp%\OEgetPrivileges.vbs"
"%temp%\OEgetPrivileges.vbs"
exit /B
:gotPrivileges
::::::::::::::::::::::::::::
:START
::::::::::::::::::::::::::::
setlocal & pushd .
cd /d %~dp0
%windir%\Microsoft.NET\Framework\v4.0.30319\InstallUtil /i "WindowsService1.exe"
pause
/i
to /u
)InstallUtil classes ( ServiceInstaller ) are considered an anti-pattern by the Windows Installer community. It's a fragile, out of process, reinventing of the wheel that ignores the fact that Windows Installer has built-in support for Services.
Visual Studio deployment projects ( also not highly regarded and deprecated in the next release of Visual Studio ) do not have native support for services. But they can consume merge modules. So I would take a look at this blog article to understand how to create a merge module using Windows Installer XML that can express the service and then consume that merge module in your VDPROJ solution.
Augmenting InstallShield using Windows Installer XML - Windows Services
Source: Stackoverflow.com