30

I have a Bin folder with many files in my project. For now, I only know how to add specific files to install folder with the code below.

<File Source='$(var.Debug)Myapplication.exe' Id='file4028276A0CAD84E3BDE449237AE31A5F' />  

However, I want to move a whole directory to the install path. For example, move the whole folder "Bin" to the install path "C:\Myapplication".

What should I do then?

Thanks in advance!

Jack He
  • 1,683
  • 3
  • 18
  • 29

3 Answers3

90

Sounds like what you want to use is the WiX tool heat, which is used to "harvest" a directory (or individual files) and create a WiX fragment file that you can use in your project.

For example, you have a directory you want to harvest, and it may include subdirectories, but there's a lot of files, and you want it all... heat will do that for you.

Consider this trivial structure:

Somedir
    |
    |---A file.txt
    |---An init file.ini
    |---another file.txt
    |---subfolder
            |
            |---a subfolder file.txt

If you use heat to harvest this directory, the tool will generate the fragment file for you that you can use as a component reference in your project without having to specify the files one at a time or use a workaround solution.

For example, the following heat command will process this directory (from one level up in this example)

heat dir somedir -o MyHarvestedStuff.wxs -scom -frag -srd -sreg -gg -cg MyComponentGroupId -dr BIN_DIR_REF

Where:

dir = harvest a directory
somedir = directory you want to harvest
-o MyHarvestedStuff.wxs = the output fragment file
-scom -sfrag -srd -sreg = Suppress COM elements, fragments, root directory as element, registry harvesting (these options will create a grouping that most applications can use)
-gg = generate GUID's now (this will put the GUID's into your output file, rather than using a wildcard "*". The advantage here is you can use non-standard TARGETDIR, which would not qualify for autogenerated GUID's)
-cg MyComponentGroupId = component group. this is what you will use in your feature set to include these files
-dr BIN_DIR_REF = this is the directory reference for which to place the files in your final package.

The resulting XML looks like this (this was run without -gg to avoid posting real GUID's)

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="BIN_DIR_REF">
            <Directory Id="dirF065D7446868E03DB0B296EBADA4E4A1" Name="subfolder" />
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="MyComponentGroupId">
            <Component Id="cmp739547000B47E975B0452D876AF7810B" Directory="BIN_DIR_REF" Guid="PUT-GUID-HERE">
                <File Id="fil09B311A6D1EABD9E94DFA5C83F59C548" KeyPath="yes" Source="SourceDir\A file.txt" />
            </Component>
            <Component Id="cmp84C8400F892D39B05EE3021CCEEAA14F" Directory="BIN_DIR_REF" Guid="PUT-GUID-HERE">
                <File Id="fil11A22646343997D26AC54171A62DFF4C" KeyPath="yes" Source="SourceDir\an init file.ini" />
            </Component>
            <Component Id="cmpFA266FC6F3269CB5D9E42C38FC995117" Directory="BIN_DIR_REF" Guid="PUT-GUID-HERE">
                <File Id="filA545B6E4B63B8211E982917FC78F6EB4" KeyPath="yes" Source="SourceDir\another file.txt" />
            </Component>
            <Component Id="cmp2EC5C1618A59F47B7BDE800EB9AA8688" Directory="dirF065D7446868E03DB0B296EBADA4E4A1" Guid="PUT-GUID-HERE">
                <File Id="filB0CD0B02385137DC806112E293083459" KeyPath="yes" Source="SourceDir\subfolder\a subfolder file.txt" />
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

In your project file, you'd have something like this under your root <Directory> element:

<Directory Id="BIN_DIR_REF" Name="bin">
    <!-- anything else you might put here...-->
</Directory>

And in your feature group:

<Feature Id="Complete" Level="1">
    ...
    <ComponentGroupRef Id="MyComponentGroupId" />         
    ...
</Feature>

Tying it all together...

  1. Supply your new fragment file to candle along with your other file(s)
  2. Supply the .wixobj file that candle produces to light
  3. Resolve the SourceDir reference in your new fragment file with a WiX preprocessor variable or with the -b option to light

    Ex: light ... -b "path to my directory that I harvested" ...

Don't let the length of the answer deter you from exploring this solution, it works quite well and it's pretty simple. Now if you want to exclude anything from that directory, that's a whole other story...

Ryan J
  • 8,275
  • 3
  • 25
  • 28
  • 1
    Thank you for your answer it really helps. I believe you meant to add ComponentGroupRef and not ComponentRef at the Feature serction. – Ravid Goldenberg Nov 05 '15 at 06:21
  • @petric oh my, yep! Thanks – Ryan J Nov 05 '15 at 06:33
  • 7
    This is beyond great information, and IMO should be marked as the correct answer. – Dave Jul 07 '16 at 14:40
  • @Ryan: Great answer it works really well. I have one issue left. I harvest my directory structure with heat as a prebuild command and resolve SourceDir with a preprocessor variable in the generated fragment file. When trying to build the project i get some errors: Directory/File XX is in the user profile but is not listed in the RemoveFile table. Any suggestions on the reason of the errors? – Dave Sep 08 '16 at 15:21
  • @Dave I have a feeling it's because you're missing a `RemoveFolder` tag for the component that cleans up after itself – Ryan J Sep 10 '16 at 04:52
  • bet this one is very stupid but have to ask: @Ryan J, when you say "this command", you are talking about Command Prompt or Command Window in VS or ? – hardyVeles Feb 02 '18 at 00:49
  • 1
    @hardyVeles I'm not sure what you're asking. All examples given were derived from running from a Windows Command Prompt, but would apply to _any_ command shell where WiX is supported. – Ryan J Feb 03 '18 at 01:54
  • Ok, I've got it. I was tripping some WiX specific script interpreter or such thing. – hardyVeles Feb 03 '18 at 09:33
  • 1
    @RyanJ, thank you for this solution, however, I met some problem while using `light` to build the `msi` file. I use the command `light.exe -b "HARVESTED_DIR"`, but it shows error `error LGHT0094 : Unresolved reference to symbol 'WixComponentGroup:HarvestedComponentId' in section`, not sure the reason. Could you please help me? I've googled some solution, too, and it seems need to combine MSbuild with Wix and add property `EnableProjectHarvesting` in the Project, is there any simple way? Thank you. – Jimmy Lin Mar 12 '18 at 03:50
  • Actually I get the same error than @JimmyLin ... if anyone know how to handle this correctly, that would be great. (Once I though Makefile where complicated ...) – Stéphane May 03 '18 at 14:46
  • @JimmyLin I found the solution : you need to pass all ```.wixobj```to ```light```, eg. ```light main.wixobj directory.wixobj -b ...``` – Stéphane May 03 '18 at 14:56
  • @Stéphane Or you can just do `light *.wixobj` – john k Jun 30 '21 at 18:48
  • @RyanJ When I do this I get an error ` error LGHT0103 : The system cannot find the file 'SourceDir\myxml.xml'.` For some reason it is replacing the directory I use in Heat (`XMLFiles` for me. In your example that would be `someDir`) with SourceDir. Any idea why? – john k Jun 30 '21 at 19:03
  • When you say "in your project file", which project file are you referring to? Are you referring to one of my .csproj files, or to my .wxs file? – awshelleyBeckman Apr 11 '23 at 17:28
2

how to include a large directory tree in a wix installer

This looks good. But too much work to do.

How do copy a folder in wix

The above link worked fine. But cannot copy sub folders.

So what I did was, I created an app which reads the whole folder and get its sub directories and files and generates the necessary code blocks like

 <Component Id="myfile" Guid="GUID">
     <File Id="myfile.txt" Source="MySourceFiles\myfile.txt" KeyPath="yes" Checksum="yes"/>
 </Component>

Then I added the generated code block to the .wxs file.

The initial time you spend for creating this app is not a waste. Because you can keep using it forever. I recommend you to copy file by file, because it's helpful during an upgrade or a repair. MSI keeps a record for each file which is to be copied. So it's maintainable and useful on upgrades and repairs.

Community
  • 1
  • 1
LeoN
  • 1,590
  • 1
  • 11
  • 19
  • 1
    So the suggestion is to continue use file block and develop an independent tool? – Jack He Oct 24 '14 at 22:34
  • Yeah. Rather than struggling to do complex things it's easy to use the tool. I'm currently doing this in my work. Within a second the code blocks get generated. – LeoN Oct 25 '14 at 15:19
  • 6
    Care to share that app?^^ – Flo Feb 09 '18 at 14:30
0

If you are running WIX via CI or command line, you can refer to: https://bardoloi.com/blog/2017/07/07/wix-ci/.

This article gives a detailed overview of what you can do with WIX.

And Step 2 depicts how to copy folders: using Heat.

zoron
  • 21
  • 4