Can I use Wix InstallService to set the recovery options for a Windows Service, and if so how ? I couldn't find any parameters for passing in the recovery option settings.
Alternately how can I run sc.exe to set the service recovery options after the install has completed from the WiX installer.
I found this Wix code elsewhere but it seems to do nothing. Where exactly in the WiX package.wsx should this go to ensure it gets called?
<CustomAction Id="SetRecoveryOptions"
Directory="INSTALLFOLDER"
Execute="immediate"
Impersonate="no"
ExeCommand="cmd.exe /c "sc failure MyWindowsPService reset= 0 actions= restart/60000/restart/60000/restart/60000""
Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="SetRecoveryOptions"
After="InstallFinalize" />
</InstallExecuteSequence>
The full Wix package file
<?xml version="1.0" encoding="UTF-8"?>
<!-- Define the variables in "$(var.*) expressions" -->
<?define Name = "My Windows Service" ?>
<?define Manufacturer = "XYZ Pty Ltd" ?>
<?define Version = "1.0.1.0" ?>
<?define UpgradeCode = "9ED3FF33-8718-444E-B44B-69A34433B7E98" ?>
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="$(Name)"
Manufacturer="$(Manufacturer)"
Version="$(Version)"
UpgradeCode="$(var.UpgradeCode)"
Compressed="true">
<!-- Allow upgrades and prevent downgrades -->
<MajorUpgrade DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit." />
<!-- Define the directory structure -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<!-- Create a folder inside program files -->
<Directory Id="ROOTDIRECTORY" Name="$(var.Manufacturer)">
<!-- Create a folder within the parent folder given the name -->
<Directory Id="INSTALLFOLDER_" Name="$(Name)" />
</Directory>
</Directory>
</Directory>
<MediaTemplate EmbedCab="yes" />
<ComponentGroup Id="ProgramFiles" Directory="INSTALLFOLDER_">
<Component Id="ABC.dll" Guid="*">
<File Id="ABC.dll" KeyPath="true" Source="$(var.MyWindowsServices.TargetDir)\ABC.dll" />
</Component>
...
</ComponentGroup>
<!-- The files inside this DirectoryRef are linked to
the App.MyWindowsService directory via INSTALLFOLDER -->
<DirectoryRef Id="INSTALLFOLDER_">
<!-- Create a single component which is the App.MyWindowsService.exe file -->
<Component Id="ServiceExecutable" Bitness="always64">
<!-- Copies the MyWindowsServices.exe file using the
project reference preprocessor variables -->
<File Id="MyWindowsServices.exe"
Source="$(var.MyWindowsServices.TargetDir)\MyWindowsServices.exe"
KeyPath="true" />
<!-- Remove all files from the INSTALLFOLDER on uninstall -->
<RemoveFile Id="ALLFILES" Name="*.*" On="both" />
<!-- Tell WiX to install the Service -->
<ServiceInstall Id="ServiceInstaller"
Type="ownProcess"
Name="MyWindowsService"
DisplayName="$(Name)"
Description="My Windows Service"
Account=".\LocalSystem"
Start="auto"
ErrorControl="normal" />
<!-- Tell WiX to start the Service -->
<ServiceControl Id="StartService"
Start="install"
Stop="both"
Remove="uninstall"
Name="MyWIndowsServices"
Wait="true" />
</Component>
</DirectoryRef>
<CustomAction Id="SetRecoveryOptions"
Directory="INSTALLFOLDER"
Execute="immediate"
Impersonate="no"
ExeCommand="cmd.exe /c "sc failure MyWindowsServices reset= 0 actions= restart/60000/restart/60000/restart/60000""
Return="asyncNoWait" />
<InstallExecuteSequence>
<Custom Action="SetRecoveryOptions"
After="InstallFinalize" />
</InstallExecuteSequence>
<!-- Tell WiX to install the files -->
<Feature Id="Service" Title="MyWindowsServices Setup" Level="1">
<ComponentRef Id="ServiceExecutable" />
<ComponentGroupRef Id="ProgramFiles" />
</Feature>
</Package>
</Wix>
EDIT:
SO I have half the answer now. This now works as long as I run msiexec from an admin command line. Any idea how I can get the installer to run the custom command with the same installer privileges since it is already running as admin.
<CustomAction Id="SetRecoveryOptions"
Directory="INSTALLFOLDER_"
Execute="immediate"
Impersonate="no"
ExeCommand="[SystemFolder]sc.exe failure MyWindowsService reset= 0 actions= restart/60000/restart/60000/restart/60000"
Return="ignore" />
<InstallExecuteSequence>
<Custom Action="SetRecoveryOptions"
OnExit="success" />
</InstallExecuteSequence>
It seems like the util:ServiceConfig element should do what you want. For example, adding to your ServiceExecutable Component:
<Component Id='ServiceExecutable' ...>
...
<util:ServiceConfig Name='YourServiceName' FirstFailureActionType='restart'
RestartServiceDelayInSeconds='60000' ... />
</Component>
That is way more robust than shelling out to sc.exe.
Update: Given access to the OP's full source code, a more complete example might look like this:
Install the Nuget Wix Util Extensions in Visual Studio Add the WiX extension's namespace following to the package.wxs file (second line)
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:util="http://wixtoolset.org/schemas/v4/wxs/util">
...
Added the util:ServiceConfig
to the service executable component
<!-- Create a single component for WindowsService.exe -->
<Component>
<!-- Installs the WindowsServices.exe file -->
<File Id="WindowsServices.exe"
Source="WindowsServices.exe" />
<!-- Tell Windows Installer to install the Service -->
<ServiceInstall Id="ServiceInstaller"
Type="ownProcess"
Name="WindowsService"
DisplayName="$(Name)"
Description="Windows Service"
Account=".\LocalSystem"
Start="auto"
ErrorControl="normal">
<!-- Update the failure handling -->
<util:ServiceConfig
FirstFailureActionType="restart"
RestartServiceDelayInSeconds="10000"
SecondFailureActionType="restart"
ThirdFailureActionType="restart"
/>
</ServiceInstall>
<!-- Tell WiX to start the Service -->
<ServiceControl Id="StartService"
Start="install"
Stop="both"
Remove="uninstall"
Name="WindowsService"
Wait="true" />
</Component>