How to create shortcuts to files harvested by heat.exe?

2.1k views Asked by At

Using WiX Toolset 3.10, and trying to create shortcuts to certain files that have been harvested with the heat.exe utility, as by:

"%WIX%\bin\heat.exe" dir SourceDir -nologo -platform x64 ^
-ke -gg -g1 -suid -srd -scom -sreg -dr INSTALLDIR ^
-cg ProjFiles -out ProjFiles.wxs

My issues:

  1. I know by now that I'm supposed to use an XSLT file to transform ProjFiles.wxs (the -t option in heat.exe) but WiX-specific documentation on how to write it is non-existent: can someone please provide an example that adds a Shortcut on the Desktop for an Id of "Prog.exe"?

  2. Because of the -g1 flag, files which share the same basename (e.g. "SourceDir\dirA\file.txt" and "SourceDir\dirZ\file.txt") will share the same Id (i.e. "file.txt"); how come this isn't a conflict, seen how the .MSI builds and runs OK?

4

There are 4 answers

0
Tom Blodget On

The WiX-specific information is documented but learning just enough XSL is a bit of a challenge. This should get you started. You might have to adapt to your names, heat arguments, etc.

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

  <xsl:output
    encoding="utf-8"
    method="xml"
    version="1.0"
    indent="yes"    
    />

  <xsl:template match='wix:Component[contains(wix:File/@Source, "SourceDir\Prog.exe")]'> 
    <!-- assumes there is only one Prog.exe -->
    <xsl:copy>
      <xsl:apply-templates select="@*|node()"/>
      <xsl:comment> added shortcut under Component with File that has Source with Prog.exe </xsl:comment>
      <!-- Elsewhere, have an Icon element like: <Icon Id="Prog.exe" SourceFile="$(var.BUILDCACHE)Bin/Prog.exe" />  -->
      <Shortcut 
        Id="ProgExeShortcut" 
        Name="Prog Application" 
        Icon="Prog.exe" 
        Directory="ProgramMenuFolder_ProgVendor" 
        Advertise="yes">
        <xsl:attribute name="WorkingDirectory"><xsl:value-of select="@Directory"/></xsl:attribute>
      </Shortcut>
      <RemoveFolder 
        Id="ProgExeShortcut_ProgramMenuFolder_ProgVendor"  
        Directory="ProgramMenuFolder_ProgVendor" 
        On="uninstall" />
    </xsl:copy>
  </xsl:template>

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

  <xsl:template match='/'>
    <xsl:comment>*** DO NOT EDIT: Generated by heat.exe; transformed by ProgComponentGroup.xsl</xsl:comment>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:template>

</xsl:stylesheet>

More specific and earlier templates match before more general or later ones. So, the base is to copy every element, attribute, text and comment AS IS, except the ones you want to change. For the ones you want to change, you reconstruct everything—in this case by copying everything the Component element had and then adding the Shortcut and RemoveFolder elements.

2
Brian Sutherland On

This should get it making a shortcut on the desktop. You'll get a ICE warning because the shortcut isn't in the same component as the Prog.exe file's component but it is safe to ignore (if you have warnings treated as errors, add the ICE to the Suppress specific ICE validations list. (wixproj properties Tool Settings in visual studio. <SuppressIces> tag in the wixproj. Or, -sice:ICE## in cmd line)

Define your DesktopFolder in the directory definitions

<Directory Id="TARGETDIR" Name="SourceDir">
    ...
    <Directory Id="DesktopFolder"/>
    ...
</Directory>

then have a component

<Component Id="ProgDesktopShortcut">
    <Shortcut
        Id="ProgDesktopShortcut"
        Directory="DesktopFolder"
        Target="[#Prog.exe]" 
        Name="Prog Shortcut"
        WorkingDirectory="INSTALLDIR" >
    </Shortcut>
    <RegistryValue  
        Id="ProgDesktopRegShortcut" 
        Root="HKCU" 
        Key="SOFTWARE\Prog\" 
        Name="ProgInstall" 
        Type="integer" 
        Value="1" 
        KeyPath="yes"/>    
</Component>    
0
huang bin bin On

replace <Element property1="1" property2="2" /> to you want to add inner tags;

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

  <xsl:output
    encoding="utf-8"
    method="xml"
    version="1.0"
    indent="yes"
    />

  <xsl:template match='wix:Component[contains(wix:File/@Source, "dv_detector_ui.exe")]'>
      <Component >
        <xsl:attribute name="Id">
          <xsl:value-of select="@Id" />
        </xsl:attribute>
        <xsl:attribute name="Guid">
          <xsl:value-of select="@Guid" />
        </xsl:attribute>
        <File>
          <xsl:attribute name="Id">
            <xsl:value-of select="wix:File/@Id" />
          </xsl:attribute>
          <xsl:attribute name="KeyPath">
            <xsl:value-of select="wix:File/@KeyPath" />
          </xsl:attribute>
          <xsl:attribute name="Source">
            <xsl:value-of select="wix:File/@Source" />
          </xsl:attribute>
          <Element property1="1" property2="2" />
        </File>
      </Component>
  </xsl:template>



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

  <xsl:template match='/'>
    <xsl:comment>*** DO NOT EDIT: Generated by heat.exe; transformed by ProgComponentGroup.xsl</xsl:comment>
    <xsl:apply-templates select="@*|node()"/>
  </xsl:template>

</xsl:stylesheet>
0
Matus On

As there is no accepted answer here, I will add my approach.

I ended up excluding the executable from heat harvesting and added it explicitly.

Filter:

<xsl:template match="wix:Component[wix:File[contains(@Source, 'Executable.exe')]]" />

Component definition:

<Fragment>
  <DirectoryRef Id="INSTALLDIR">
    <Component Id='Executable' Guid='3E4D4C89-EB4C-4B0C-88C0-7B9A239FD342'>

      <File Id='ExecutableId'
            DiskId='1'
            Source='$(var.xx.TargetPath)'
            KeyPath='yes'>

        <Shortcut Id="ProgramDirShortcutId"
                  Directory="ProgramMenuDir"
                  Name="Application name"
                  WorkingDirectory='INSTALLDIR'
                  Icon="Icon.ico"
                  Advertise="yes" />

        <Shortcut Id="DesktopShortcutId"
                  Directory="DesktopFolder"
                  Name="Application name"
                  WorkingDirectory='INSTALLDIR'
                  Icon="Icon.ico"
                  IconIndex="0"
                  Advertise="yes" />

      </File>
    </Component>
  </DirectoryRef>
</Fragment>