28

I'm new in Wix, I succefully create an MSI installer for my project, but my Bin folder have a lot of DLL's files with EXE main file, I want to include all these files with the installer

I found THIS solution, that seems right but unfortunately I can not accomplish this solution in my Wix file, Here's my Wix file:

<Product Id="*" Name="Setup"
       Language="1033" Version="1.0.1.0"
       Manufacturer="ORDER MS"
       UpgradeCode="a4f0a0d0-ae64-4f62-9bb3-efa7e75072e0">

<Package InstallerVersion="200"
         Compressed="yes"
         InstallScope="perMachine" />

<MajorUpgrade Schedule="afterInstallInitialize"
              DowngradeErrorMessage="A newer version of [ProductName] is already installed." />

<MediaTemplate />

<Feature Id="ProductFeature" Title="Setup" Level="1">
  <ComponentGroupRef Id="ProductComponents" />
  <ComponentRef Id="ApplicationShortcutDesktop" />
  <ComponentRef Id="ApplicationShortcut" />
</Feature>

<Icon Id="Icon.exe" SourceFile="$(sys.CURRENTDIR)\icon.ico"/>
<Property Id="ARPPRODUCTICON" Value="icon.exe" />



<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
  <Component Id="ProductComponent">
    <File Source="$(var.Order.TargetPath)" />
  </Component>

  <Component Guid="A7C42303-1D77-4C70-8D5C-0FD0F9158EB4" Id="CopyComponent">
    <CopyFile Id="SomeId" 
              SourceProperty="SOURCEDIRECTORY"
              DestinationDirectory="CopyTestDir" SourceName="*" />
  </Component>    
</ComponentGroup>

I get this Error:

Error 1 ICE18: KeyPath for Component: 'CopyComponent' is Directory: 'INSTALLFOLDER'. The Directory/Component pair must be listed in the CreateFolders table.

BlueSun
  • 3,541
  • 1
  • 18
  • 37
Abdulsalam Elsharif
  • 4,773
  • 7
  • 32
  • 66

4 Answers4

70

This solution works with WIX 3.11.

To harvest an entire directory, you can use Heat from the WIX toolset. With Heat we can automatically include all files from a given source directory on every build. To do that we first need to edit the Setup.wixproj:

Define the Harvestpath for Heat:

<PropertyGroup>    
  <DefineConstants>HarvestPath=...\Deploy</DefineConstants>
</PropertyGroup>

Heat will create a .wxs file. So we need to add this file to the compile ItemGroup:

<ItemGroup>
  <Compile Include="Product.wxs" /> <!-- This will be your default one -->
  <Compile Include="HeatGeneratedFileList.wxs" /> <!-- This is the Heat created one -->
</ItemGroup>

Then execute Heat in the BeforeBuild build target:

<Target Name="BeforeBuild">
  <HeatDirectory Directory="..\Deploy" 
    PreprocessorVariable="var.HarvestPath" 
    OutputFile="HeatGeneratedFileList.wxs" 
    ComponentGroupName="HeatGenerated" 
    DirectoryRefId="INSTALLFOLDER" 
    AutogenerateGuids="true" 
    ToolPath="$(WixToolPath)" 
    SuppressFragments="true" 
    SuppressRegistry="true" 
    SuppressRootDirectory="true" />  
</Target>

This will generate the HeatGeneratedFileList.wxs every time the WIX installer is built. The directory ..\Deploy has to be set to the directory of the files to include. The only thing we have to do to include these files in our installer is to edit the main .wxs file (like Product.wxs in this example). Heat will create a ComponentGroup with the given name from above. This component needs to be referenced in the Feature section of the Product.wxs:

<Feature Id="ProductFeature" Title="DiBA Tool" Level="1">
  <...>
  <ComponentGroupRef Id="HeatGenerated" />
</Feature>
David Bond
  • 149
  • 1
  • 9
darkend
  • 1,058
  • 13
  • 17
  • For me this resulted in a HEAT5052 error The directory 'Deploy' could not be found. The solution is to change when the heat command runs as described here: https://stackoverflow.com/questions/34282907/wix-heat-pre-build-event-fires-too-early-on-build-server – Scott Langham Oct 09 '17 at 13:09
  • 3
    Works like a charm. If you hate having to specify twice the same 'deploy' path, you can define a property once (in the `PropertyGroup` section) and refer to it (as `$(MyDeployPath)`) any number of times in the project file. – Gabriele Giuseppini Jan 27 '19 at 19:07
  • 1
    Spent a lot of hours trying to understand how it works, read a lot of tutorials and articles but your description is the best so far! – Edi May 09 '19 at 19:38
  • 1
    It works, but I don't understand why there are 3 dots in the HarvestPath constant, or why it could not be reused in the HeatDirectory.Directory property. – Rye bread Jul 03 '20 at 08:32
  • @Rugbrød the ellipses are indicating that other ComponentGroupRef tags may be present. And to your second question, the second usage of the path is actually part of the generated files. and must be defined as a constant in order to be available to the generated file for referencing https://sourceforge.net/p/wix/mailman/message/26690357/ – Jody Sowald Dec 15 '20 at 17:16
  • 1
    a convenient way to not duplicate the original path is to set the path to a property and include the property as a constant ..\somepath PropertyName1=$(PropertyName1);PropertyName2=$(PropertyName2); – Jody Sowald Dec 15 '20 at 17:24
  • 3
    For errors (and VS crashes) on x64 see – MrTux Jan 02 '22 at 15:51
  • Specifically: https://github.com/wixtoolset/issues/issues/2467#issuecomment-736519622 So in the *.wixproj add `true` and `RunAsSeparateProcess="$(RunWixToolsOutOfProc)"` to the `` element. – Martin Schneider Sep 22 '22 at 12:44
  • If you are using Visual Studio 2022 (or newer) you will need to also add the following to the before build, HeatDirectory parameters as per https://github.com/wixtoolset/issues/issues/6636 : ```RunAsSeparateProcess="true"``` – SteveC Mar 20 '23 at 04:48
8

I think you've gotten into a bit of a muddle, if you don't mind me saying so.

CopyFile will copy a file will copy a file from one place on a target machine (the machine where the install is being installed, not your development computer) to another folder on the same machine. I don't think this is what you want.

As Brian suggested you can use Heat to scan a folder and generate some code for you. You can use that in one of two ways:

As a development aid

Run the tool with this kind of command:

heat dir ".\My Files" -gg -sfrag -template:fragment -out directory.wxs

Then, take directory.wxs and use it as source code.

In the build pipeline

You can use the Heat tool in the build pipeline, so that compiling the install will generate the code.

Contrary to Brian's suggestion, if you are using MSBuild I would suggest the HarvestDirectory target. Here's what I have where I am doing something similar:

<HarvestDirectory Include="$(MyHarvestDirectory)">
  <InProject>false</InProject>
  <PreprocessorVariable>var.MyHarvestDirectory</PreprocessorVariable>
  <ComponentGroupName>MyComponentGroup</ComponentGroupName>
  <DirectoryRefID>MY_DIRECTORY_ID</DirectoryRefID>
</HarvestDirectory>

This will feed an item into the HarvestDirectory target and make sure it's all handled in the correct way. This code just goes into an ItemGroup in your .wixproj. (If you're not sure how to tweak your project file, check out this video)

Of course, this assumes you are using MSBuild (or Visual Studio, because that uses MSBuild).

Caveat

Having said all this, if the main issue is simply that there are lots of files, I would simply say knuckle down and just define the list. Use the Heat tool as a scaffold if you like, but there's no substitute for just learning the language and working with it. Trying to do things with auto generated code can introduce subtle issues, and it's always simpler to work with a static list.

JohnL
  • 3,922
  • 3
  • 22
  • 22
6

I do something similar to what you require here during my installation. I need to copy the contents of a folder with 1000+ files in it (the help files).

What I did to solve this is the following:

In the Installer.wixproj I defined the following:

<Target Name="BeforeBuild" >
    <Exec Command="&quot;$(WixToolPath)Heat.exe&quot; dir &quot;$(MSBuildThisFileDirectory)\$(Configuration)\bin&quot; -ag -cg BinDir -dr BIN -template fragment -sreg -sfrag -srd -var var.BinDir -o &quot;$(MSBuildThisFileDirectory)\Components\Bin.wxs&quot;" Condition="!Exists('$(MSBuildThisFileDirectory)\Components\Bin.wxs')" />
</Target>

And this will run heat on the $(Configuration)\bin\ dir and generate a wxs file including ALL the files in the bin dir.

This way if you add or delete any binaries in your bin dir it will automatically get picked up when you rebuild your installer (if the Bin.wxs file doesn't exist).

Now you need to make sure you define the variable "BinDir" for wix which points to the bin dir on the build machine. You also need to add the Bin.wxs file to your wixproj as a link (when adding existing file there's a tiny arrow drop down on "Add". Click that and select "Add as link".)

I think there's an actual heat target somewhere in wix but I haven't looked through that enough to know how to use it yet.

Brian Sutherland
  • 4,698
  • 14
  • 16
-2

As an alternative to the other answers:

There is a Visual Studio extension called Wax (GitHub, Visual Studio Marketplace):

enter image description here

It provides an alternative way to handle all the required files with GUI.

It may be not as sophisticated as command-line tools like heat, but it is much simpler and can be a nice and friendly tool in the beginning, when you just start learning WiX - and want a quickstart.

simon
  • 1,161
  • 10
  • 27
  • That was a complete waste of time, it just shows warning symbols and does not let me open or select anything. – Paul McCarthy Jun 21 '21 at 13:55
  • @PaulMcCarthy, I use it for speeding up creating my WiX installers. In my experience, it works very well :) – simon Jul 07 '21 at 10:21