Search code examples
wixwindows-installer

Looking for a simple way to include all files in a project's BIN folder in my MSI


I have many files (hundreds) in my project's output BIN folder. I simply need to have an installer to include the files in the bin folder in an MSI installer.

In my WIX installer project, I have the following target to use the harvest tool and generate a list of all files in a bin folder, later on, I reference them in my WIX definitions:

  <Target Name="GenerateHeat">
    <HeatDirectory Directory="..\MyApp\bin\Debug" 
       PreprocessorVariable="var.HeatPath" OutputFile="HarvestedFiles.wxs"
       ComponentGroupName="HarvestedFiles" DirectoryRefId="FOLDER1" 
       AutogenerateGuids="true" ToolPath="$(WixToolPath)" 
       SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" />
  </Target>

Is there any way to simply include all files in a bin folder and include them in the MSI without generating the intermediate file list? I prefer to specify the BIN folder name and WIX includes them in a <ComponentGroup>, so I can reference it in my <Product>

Update and clarification

This question is not about how MSI works. This is about how WIX can copy the content of a folder into an MSI without specifying every single file name in a <Component> and <File> sandwich.


Solution

  • Is there any way to simply include all files in a bin folder and include them in the MSI without generating the intermediate file list?

    This is not possible with built-in functionality of the free version of WiX. As Stein Åsmul points out, the commercial branch of WiX might have something like that.

    If commercial WiX is not an option and you are ready to invest significant time in C# development, using mostly undocumented API, you could write a WiX compiler extension that adds entries to the File and Component tables based on a given source directory path. It could also generate component groups, that can be referenced elsewhere.

    I have done exactly this in the past but it certainly wasn't a trivial task. One should also have very good knowledge of component rules and MSI in general before doing things like generating component GUIDs. You will find some pseudo code below. Before going down this route, it would be worth looking around if someone else has created an open-source WiX extension like that.

    This is the kind of authoring that could be achieved with such a compiler extension:

    <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
         xmlns:ex="MyExtensionUri">
      <Product ... >
        <Feature Id="ProductFeature" Title="MyFeature" Level="1">
          <ComponentGroupRef Id="ProductComponents" />
        </Feature>
      </Product>
    
      <Fragment>
        <ex:ComponentGroupFromFiles Id="ProductComponents"
          Directory="INSTALLFOLDER"
          Source="MyApp\bin"
          Wildcard="*"/>
      </Fragment>
    </Wix>
    

    Here is some pseudo code for a compiler extension. This is mainly intended to serve as keywords for exploring the WiX source "Compiler.cs".

    Override Wix.CompilerExtension.ParseElement() to parse the attributes of your extension element.

    Create component group and reference it by the product:

    Wix.Row compGroupRow = Core.CreateRow(sourceLineNumbers, "WixComponentGroup");
    compGroupRow[0] = myComponentGroupId;
    
    Core.CreateWixGroupRow( sourceLineNumbers, Wix.ComplexReferenceParentType.Product, Core.ActiveSection.Id, Wix.ComplexReferenceChildType.ComponentGroup, myComponentGroupId );
    

    For each component/file:

    // Add record to the Component table
    Wix.Row compRow = Core.CreateRow( sourceLineNumbers, "Component" );
    // TODO: Assign data to compRow[0..5] according to MSI "Component" table documentation
    
    // Add this component to the component group.
    Core.CreateComplexReference( sourceLineNumbers, Wix.ComplexReferenceParentType.ComponentGroup, myComponentGroupId, "", Wix.ComplexReferenceChildType.Component, myComponentId, false );
    
    // Add record to the File table.
    Wix.Row fileRow = Core.CreateRow( sourceLineNumbers, "File" );
    // TODO: Assign data to fileRow[0..2] and [6] according to MSI "File" table documentation. Columns 3, 4, 5, 7 are written by the WiX binder at a later time! Set them to null (if nullable) or 0.
    
    // Create required metadata for WiX
    Wix.WixFileRow wixFileRow = (Wix.WixFileRow) Core.CreateRow(sourceLineNumbers, "WixFile");
    // TODO: Assign wixFileRow.File, wixFileRow.Directory, wixFileRow.DiskId, wixFileRow.Source
    //       Set wixFileRow.Attributes to 1 if you have generated a short file name.
    
    // Add reference to the Media table
    Core.CreateWixSimpleReferenceRow( sourceLineNumbers, "Media", diskId );
    

    Useful utilities for generating Component / File table column data:

    Core.GenerateIdentifier()
    Core.GenerateShortName()
    

    How to add components to a merge module? This is left as an exercise for the reader. 😉 Just find the code in WiX's "Compiler.cs".