WiX Installer: using xslt with heat.exe to update attributes
Asked Answered
F

4

15

I am trying to create a WiX installer for a Windows service, and I have read that I need to set the KeyPath to “no” for all my files, with the exception of the .exe in my WiX script. I am currently generating my Directory and file structure using Heat.exe here is my command:

"$(WIX)bin\heat.exe" dir $(SolutionDir)EmailGenerationService\bin\PROD 
                    -cg EmailGenFiles -gg -scom -sreg -sfrag -srd -suid 
                    -dr INSTALLLOCATION -var var.FileSource 
                    -t $(Projectdir)KeyPathTransform.xslt 
                    -out $(ProjectDir)DirectoryAndFileComponents.wxs

It is my intention to update all the file elements with Keypath=”no” in my DirectoryAndFileComponents.wxs file. A sample of the output from heat is:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <DirectoryRef Id="INSTALLLOCATION">
      <Component Id="Dollar.Common.dll" Guid="{2BCD0767-2383-47CF-B1BF-325FA4A3264F}">
        <File Id="Dollar.Common.dll" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.dll" />
      </Component>
      <Component Id="Dollar.Common.Exceptions.dll" Guid="{B7238091-76D1-42F5-A3B4-A539DFF3BD92}">
        <File Id="Dollar.Common.Exceptions.dll" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.Exceptions.dll" />
      </Component>
      <Component Id="Dollar.Common.Exceptions.pdb" Guid="{43711979-747D-49C9-BAE4-ECD44FAF5E67}">
        <File Id="Dollar.Common.Exceptions.pdb" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.Exceptions.pdb" />
      </Component>
      <Component Id="Dollar.Common.Logging.dll" Guid="{59F9ABF3-5F68-410C-BC96-0556282F1E04}">
        <File Id="Dollar.Common.Logging.dll" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.Logging.dll" />
      </Component>

Here is the XSLT I am passing to heat to perform the transformation:

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" 
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:msxsl="urn:schemas-microsoft-com:xslt" 
            exclude-result-prefixes="msxsl" 
            xmlns:wix="http://schemas.microsoft.com/wix/2006/wix"
            xmlns:my="my:my">

  <xsl:output method="xml" indent="no"/>

  <xsl:strip-space elements="*"/>

  <xsl:template match="@*|node()">
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match='/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Component/wix:File[@Id and not (@Id="EmailGenerationService.exe")]'>
    <xsl:attribute name="KeyPath">
          <xsl:value-of select="no"/>
    </xsl:attribute>
  </xsl:template>
</xsl:stylesheet>

I have tried quite a few variations of this based on other posts on this site and else where, but as yet have been unable to get the file created by heat.exe to have KeyPath=”no”.

Am I missing something obvious?

Fitts answered 7/11, 2011 at 9:40 Comment(0)
T
17

You have two different defined namespaces:

  1. In XML: http://schemas.microsoft.com/wix/2006/wi
  2. In XSLT: http://schemas.microsoft.com/wix/2006/wix

As far as I know, correct namespace for WiX is http://schemas.microsoft.com/wix/2006/wi. So you should change your XSLT.

XSLT:

<xsl:stylesheet version="1.0"
            xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
            xmlns:msxsl="urn:schemas-microsoft-com:xslt"
            exclude-result-prefixes="msxsl"
            xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
            xmlns:my="my:my">

    <xsl:output method="xml" indent="yes" />

    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match='wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Component/wix:File[@Id and not (@Id = "EmailGenerationService.exe")]'>
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="KeyPath">
                <xsl:text>no</xsl:text>
            </xsl:attribute>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Input XML:

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLLOCATION">
            <Component Id="Dollar.Common.dll" Guid="{2BCD0767-2383-47CF-B1BF-325FA4A3264F}">
                <File Id="Dollar.Common.dll" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.dll" />
            </Component>
            <Component Id="Dollar.Common.Exceptions.dll" Guid="{B7238091-76D1-42F5-A3B4-A539DFF3BD92}">
                <File Id="Dollar.Common.Exceptions.dll" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.Exceptions.dll" />
            </Component>
            <Component Id="Dollar.Common.Exceptions.pdb" Guid="{43711979-747D-49C9-BAE4-ECD44FAF5E67}">
                <File Id="Dollar.Common.Exceptions.pdb" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.Exceptions.pdb" />
            </Component>
            <Component Id="Dollar.Common.Logging.dll" Guid="{59F9ABF3-5F68-410C-BC96-0556282F1E04}">
                <File Id="Dollar.Common.Logging.dll" KeyPath="yes" Source="$(var.FileSource)\Dollar.Common.Logging.dll" />
            </Component>
        </DirectoryRef>
    </Fragment>
</Wix>

Output XML:

<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Fragment>
    <DirectoryRef Id="INSTALLLOCATION">
      <Component Id="Dollar.Common.dll" Guid="{2BCD0767-2383-47CF-B1BF-325FA4A3264F}">
        <File Id="Dollar.Common.dll" Source="$(var.FileSource)\Dollar.Common.dll" KeyPath="no" />
      </Component>
      <Component Id="Dollar.Common.Exceptions.dll" Guid="{B7238091-76D1-42F5-A3B4-A539DFF3BD92}">
        <File Id="Dollar.Common.Exceptions.dll" Source="$(var.FileSource)\Dollar.Common.Exceptions.dll" KeyPath="no" />
      </Component>
      <Component Id="Dollar.Common.Exceptions.pdb" Guid="{43711979-747D-49C9-BAE4-ECD44FAF5E67}">
        <File Id="Dollar.Common.Exceptions.pdb" Source="$(var.FileSource)\Dollar.Common.Exceptions.pdb" KeyPath="no" />
      </Component>
      <Component Id="Dollar.Common.Logging.dll" Guid="{59F9ABF3-5F68-410C-BC96-0556282F1E04}">
        <File Id="Dollar.Common.Logging.dll" Source="$(var.FileSource)\Dollar.Common.Logging.dll" KeyPath="no" />
      </Component>
    </DirectoryRef>
  </Fragment>
</Wix>
Tareyn answered 7/11, 2011 at 10:15 Comment(1)
Thank you for the response, but this also is not working. Heat runs ok with out errors, but the resulting file still has KeyPath="yes" on all nodes.Fitts
D
2

I won't answer your original question. :)

I have read that I nned to set the keyPath to “no” for all my files, with the exception of the .exe

I think you were mislead. In reality the ServiceInstall table has a column Component_, and according to MSDN:

to install this service using the InstallService table, the KeyPath for this component must be the executable file for the service.

It doesn't mean the non-exe files in other components should have @KeyPath='no'. It just says that the EXE file of the service should reside in a separate component and must be the key path of it.

The key path is a very important concept of the MSI technology. You can read more about it here, see the description of the KeyPath column.

Now, if we get back to your original question - no, you don't have to tweak the heat output the way you mentioned. It will generate the WiX authoring you need by default.

Dirham answered 7/11, 2011 at 10:33 Comment(3)
Thanks for the info Yan, I took my asumptions from here: blog.tentaclesoftware.com/archive/2009/01/01/21.aspxFitts
In the sample you reference a single component contains a number of files. It is true that only one file can be marked as KeyPath='yes', but you don't have to explicitly mark others as KeyPath='no'. And in the sample heat.exe generated for you your assumption is purely wrong.Dirham
@Mark Jones, sorry if my previous comment seems to be expressed in a tough form. I simply can't resist reacting when I see people spend time and efforts trying to solve a problem which doesn't exist :)Dirham
F
1

May I suggest a different approach?

<xsl:template match="@KeyPath[parent::wix:File[parent::wix:Component[parent::wix:DirectoryRef[parent::wix:Fragment[parent::wix:Wix]]]] and . != 'EmailGenerationService.exe']">
        <xsl:attribute name="KeyPath">
            <xsl:value-of select="'no'"/>
        </xsl:attribute>
</xsl:template>

Just change your template matching to the above and you should have the correct result.

Fagen answered 7/11, 2011 at 10:40 Comment(1)
this transformation works good, but pay attention because you can't use generated Guid (Guid="*") with Keypath=No (KeyPath is used by Heat to generate Guid)Duggins
N
1

I was wondering why this did not work for me with wix4 until i noticed the following. Granted that i do work with the following removal expression:


    <xsl:key name="appsettingssearch" match="wix:Component[contains(wix:File/@Source, '$(var.ArtifactsPathWeb)\appsettings.json')]" use="@Id" />
    <xsl:template match="wix:Component[key('appsettingssearch', @Id)]" />
    <xsl:template match="wix:ComponentRef[key('appsettingssearch', @Id)]" />

I noticed that in my xslt i had xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"

while in my wxs i had generated the xml namespace xmlns="http://wixtoolset.org/schemas/v4/wxs"

Once i changed the namespace to be the same it worked flawlessly.

Dropping this here so others who use wix4 find a quick solution

Nicholas answered 21/11, 2023 at 21:36 Comment(1)
This cannot be upvoted enough. I've spent two whole days trying to get it to work until I came across this.Capper

© 2022 - 2024 — McMap. All rights reserved.