Craig
Craig

Reputation: 440

How do I use WiX to install PeterKottas.DotNetCore.WindowsService?

I have built a windows service in DotNet Core that is installed with the Peter Kottas WindowsServer nuget (https://github.com/PeterKottas/DotNetCore.WindowsService). In order to install the service you have to publish the code, deploy it wherever, run cmd prompt with administrator rights, move directory to the deployed code and execute the following line: "MyService action:install"

<CustomAction Id="CallCmd" Value="[SystemFolder]cmd.exe" Directory="MYSERVICE" />
<CustomAction Id="MoveDirectory" Directory="MYSERVICE" ExeCommand="cd C:\Program Files (x86)\MYCOMPANY\MYSERVICE"/>
<CustomAction Id="CA_InstallMyService" Directory="MYSERVICE" ExeCommand="MyService action:install" />

<InstallExecuteSequence>
  <Custom Action="CallCmd" After="PublishProduct" />
  <Custom Action="MoveDirectory" After="CallCmd" />
  <Custom Action="CA_InstallMyService" After="MoveDirectory" />
</InstallExecuteSequence>

Looking in Orca it appears to be sitting in the correct order, as these instructions can't browse to the folder until it has been created.

enter image description here

However, on executing the MSI and after clicking "Install" it gets halfway through, fails and runs backwards through the installation process. (I've tried watching the "Program Files (x86)" directory and do not even see my directory folder structure stated in my Product.wxs being created.

A little digging around in the event viewer shows this error:

Error 1721. There is a problem with this Windows Installer package. A program required for this install to complete could not be run. Contact your support personnel or package vendor. Action: MoveDirectory, location: C:\WINDOWS\SysWOW64\cmd.exe\, command: cd C:\Program Files (x86)\MYCOMPANY\MYSERVICE

Upvotes: 0

Views: 489

Answers (3)

Craig
Craig

Reputation: 440

While I agree with the answer provided by PhilDW that the most ideal way to handle windows services is to use the functionality provided by WiX, the problem here is that DotNetCore is designed to be multi-platform and currently does not contain any of the .Net Standard assemblies for windows services, hence why I've used Peter Kottas's nuget package to implement the windows service. A way round this could be to use a docker container - however, this is out of scope for the current version, so here is how I over came this issue:

TLDR: I called the install command via CustomAction when the user exits the application. This requires Administration rights, so the appropriate flags have been set in the MSI and the MSI is always ran using a Bootstrapper (Burn) application to give these rights in a user friendly manour.

First, I created a custom action to fire the install command in a command prompt.

<CustomAction Id="CA_InstallService" Directory="INSTALLDIR" Return="ignore" ExeCommand="cmd /s /c &quot;MyService action:install start-immediately:false&quot;" />

I then created a new WiX UI element. It's WixUI_Mode is "FeatureTree" and I added an extra "DoAction" property to the exit dialog to call the customaction when the user clicks Finish.

<Publish Dialog="ExitDlg" Control="Finish" Event="DoAction" Value="CA_InstallService" Order="990"></Publish>

The installer requires admin rights to call this, so in the Product.wxs file set the "InstallPrivileges" attribute on the Package element to "elevated". Now if you call the msi via command prompt with admin privileges this will successfully call the "action:install". OR you can create a bootstrapper project that references your wix installer and in the Bundle.wxs file on the "MsiPackage" set the "ForePerMachine" attribute to "yes".

See comments for some loosely related side notes from myself.

Upvotes: 0

PhilDW
PhilDW

Reputation: 20780

This is not a good design paradigm considering Windows Installer (and WiX) has built-in support for installing services, as well as starting and stopping them so they can be replaced or uninstalled. That nuget might be an interesting service model, but the start, stop, and deployment isn't the recommended way to install a service using Windows Installer. There's simply no need to run code.

See WiX ServiceInstall and ServiceControl elements.

Upvotes: 4

Brian Sutherland
Brian Sutherland

Reputation: 4798

You must mark your CustomActions as Execute="Deferred"

The InstallExecuteSequence happens in two parts, the first is a planning phase where it figures out what it will be doing and the second part is running the plan script it just made in an elevated context.

If you plan some custom actions in the InstallExecuteSequence but do not mark them deferred, they will run during the planning part of the execute sequence which is before any of the files have been installed because the InstallFiles standard action requires elevation and that happens in the 2nd part of the sequence when it runs the planned script.

Generally you use this pattern when you need to run an elevated custom action that needs property values from your install. You schedule two custom actions, one deferred and one not, where the non-deferred action actually sets some special values to be used by the deferred action when it executes.

I haven't used the Directory attribute for CustomActions before so you may need to re-write some of these actions to get them to work, I'm not sure.

This other question's answer should help explain a bit how deferred custom actions get values at runtime from properties of the installer if you need to change the way your custom actions work.

WiX - commit more than one Property to deferred Custom Action

Upvotes: 0

Related Questions