3
votes

I have a WiX installer to install and start a service. However, all the examples I find place the ServiceInstall tag directly below the file tag for the .exe file to be installed as a service. I can’t do this as I am using heat to generate my file elements in a separate file. So my wix script looks like this:

 <Directory Id="INSTALLLOCATION" Name="Email Generation Service">
        <Component Id="SetupService" Guid="51E78696-80E0-4CDA-8F49-902C67CB129C">
          <CreateFolder  />
          <ServiceInstall Id="ServiceInstaller"
                          Type="ownProcess"
                          Vital="yes"
                          Name="EmailGenerationService"
                          DisplayName="Email Generation Service"
                          Description="Service for generating Emails from Nexus"
                          Start="auto"
                          Account="LocalService"
                          ErrorControl="ignore"
                          Interactive="no">
          </ServiceInstall>
          <ServiceControl Id="StartService" Start="install" Stop="both" Remove="uninstall" Name="EmailGenerationService" Wait="yes" />
        </Component>
 </Directory>

How can I tell WiX which file I want to install as a service?

I have used XSLT to set the KeyPath on all files to no, with the exception of the file I want to install, despite the fact that all files are in their own component. I am at a bit of a loss now :(

2

2 Answers

2
votes

A service must be linked to a specific file. This is a Windows Installer limitation. So one way or another you need to create the ServiceInstall element under your EXE file element.

A solution would be to hard-code the EXE file instead of letting it be generated automatically.

1
votes

Adding this as relevant information, I had a similar problem and used a similar solution to you, adding an xml transform. However, I used the transform to insert the service control/install elements into the heat-generated fragment. I've pasted my transform below, you may have to modify properties or remove items you don't need.

Some things to note:

  • The service control doesn't auto-start the service on install, I had issues with assembly refs not being populated by the time the msi tried to invoke them
  • A key part of the XSLT adds an "include" statement at the top of the heat.exe file so that I can reference variables from my .wxi file
  • This assumes the variable name you told heat to inject as your -var parameter is "var.SourcePath"
  • I never extracted the name of the .exe file or settings file out as variables that can be injected into the transform because they were fairly stable and it seemed...difficult
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" 
  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
  xmlns:wix="http://schemas.microsoft.com/wix/2006/wi" 
  xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

  <!-- Template for the new ServiceInstall element -->
  <xsl:param name="pServiceInstall">
    <xsl:element name="ServiceInstall">
      <xsl:attribute name="Id">SVINSTL_$(var.ServiceName)</xsl:attribute>
      <xsl:attribute name="Description">$(var.ServiceInstallDescription)</xsl:attribute>
      <xsl:attribute name="Account">$(var.SystemAccount)</xsl:attribute>
      <xsl:attribute name="DisplayName">$(var.ServiceInstallDisplayName)</xsl:attribute>
      <xsl:attribute name="ErrorControl">normal</xsl:attribute>
      <xsl:attribute name="Name">$(var.ServiceName)</xsl:attribute>
      <xsl:attribute name="Interactive">no</xsl:attribute>
      <xsl:attribute name="Start">auto</xsl:attribute>
      <xsl:attribute name="Type">ownProcess</xsl:attribute>
      <xsl:attribute name="Vital">yes</xsl:attribute>
    </xsl:element>
  </xsl:param>

  <!-- Template for the new ServiceControl element -->
  <xsl:param name="pServiceControl">
    <xsl:element name="ServiceControl">
      <xsl:attribute name="Id">SVCTRL_$(var.ServiceName)</xsl:attribute>
      <xsl:attribute name="Name">$(var.ServiceName)</xsl:attribute>
      <xsl:attribute name="Stop">both</xsl:attribute>
      <xsl:attribute name="Remove">uninstall</xsl:attribute>
      <xsl:attribute name="Wait">yes</xsl:attribute>
    </xsl:element>
  </xsl:param>

  <!-- Insert a ?include statement at the top of the fragment so it can use config variables -->
  <xsl:template match="wix:Wix">
    <xsl:copy>
      <xsl:processing-instruction name="include">InstallerSettings.wxi</xsl:processing-instruction>
      <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <!-- Turn the file name of the executable into a variable so it can be targeted for shortcuts,etc -->
  <xsl:template match="@Source[. ='$(var.SourcePath)\HARD_CODED_NAME_OF_PROJECT.exe']">
    <xsl:call-template name="identity" />
    <xsl:attribute name="Id">$(var.ServiceExecutableFileId)</xsl:attribute>
  </xsl:template>

  <!-- Insert the ServiceInstall and ServiceControl elements into the component with the exe file -->
  <xsl:template match="//wix:File[@Source='$(var.SourcePath)\HARD_CODED_NAME_OF_PROJECT.exe']">
    <xsl:call-template name="identity" />
    <xsl:copy-of select="$pServiceInstall"/>
    <xsl:copy-of select="$pServiceControl"/>
  </xsl:template>

  <!-- Identity template (copies everything as is) -->
  <xsl:template match="@*|node()" name="identity">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()" />
    </xsl:copy>
  </xsl:template>

</xsl:stylesheet>