0

Having an installer which allows the user to select a zip file from disk, we'd like to unzip that file into installation target directory.

Is this possible with standard wix toolset / extensions? Do we need to create a custom action?

Currently, we are doing stuff like this CreateObject("Shell.Application"), then use it like this objShell.NameSpace(installDir).CopyHere(objShell.NameSpace(configPath).items), 20

But I feel it's not a good way to do it and besides it also has issues with windows UAC.


EDIT: The motivation is to have a single msi installer and several customization zips. When the customer installs the application, he gets the msi and one of the zips and he selects the zip at install time (or sends as parameter in an automated install).

František Žiačik
  • 7,511
  • 1
  • 34
  • 59
  • 4
    Unzipping a zip during installation seems like a bad idea in general. – Brian Sutherland Jun 05 '18 at 15:37
  • @BrianSutherland you're right, the thing is we have one installer which is to be installed with additional stuff for each customer. The additional stuff is in the zip file. I agree it's not very good way to do it, but it's how it is right now. – František Žiačik Jun 06 '18 at 14:53
  • @FrantišekŽiačik your comment makes me think you are trying to deploy custom artifacts for (potentially) each client. I've written an answer that addresses this scenario. If this in fact the case, could you update your original question to clarify the intent of unzipping the file (i.e. is it to allow you, the installer author, to customize the install per client, or is it to allow the client, the installer consumer, to customize their install by selecting an arbitrary zip file to deploy). – pixelTitan Jun 19 '18 at 12:40
  • 1
    Just added an answer with some information about pre-processor constructs - to check if this is what you really need to implement your setup in a flexible way. – Stein Åsmul Jun 19 '18 at 15:21
  • 1
    @pixelTitan added info about the intent – František Žiačik Jun 20 '18 at 07:49
  • What you could do, is use either @SteinÅsmul or my answer to generate multiple builds of your installer that each would include a certain customization, as well as a base installer which includes no customization. Then, you could generate patches between the base installer and each of the customized installers. When deploying the installer, you could then send the base installer, with each of the patches. The user could then install the base product, then choose which patch to install in order to customize the product. – pixelTitan Jun 20 '18 at 20:22
  • This would also have the added benefit of allowing the user to uninstall a patch, thus removing the customization, or, depending on how the patch was written, allow the user to change which patch is installed, overwriting the previously installed patch. – pixelTitan Jun 20 '18 at 20:23

2 Answers2

0

A pattern I like to use is creating an extension point in my WiX installer, called Extension.wxs. The default (or standard) version of this file looked like the following:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLLOCATION">
            <!-- Setup all of the extension components here. -->
            <!-- For example...
            <Component Id="INSERT_COMPONENT_ID_HERE" Guid="{8451AFA1-5185-468D-B0A0-650494251D8F}">
                <File Id="INSERT_FILE_ID_HERE" KeyPath="yes" Source="SomeExtension.dll" />
            </Component> -->
        </DirectoryRef>             
    </Fragment>
    <Fragment>
        <ComponentGroup Id="ExtensionComponentGroup">
            <!-- Reference components that are extending the standard product here -->
            <!-- <ComponentRef Id="INSERT_COMPONENT_ID_HERE" /> -->            
        </ComponentGroup>
    </Fragment>
</Wix>

Wherever your Feature is defined that you want to customize, you would add a reference to this ExtensionComponentGroup, like so:

<Feature Id="MyMainFeature"
         Title="My Main Feature"
         Level="1"
         Description="An awesome feature, needed to run the application.">
  <ComponentGroupRef Id="MyMainComponentGroup" />
  <ComponentGroupRef Id="ExtensionComponentGroup" />
</Feature>

In my case, I just use a set of Powershell scripts to build out my installer, because it makes it easier to dynamically determine what to feed into the WiX Toolset CLI. So, my standard invocation of candle.exe would look like the following:

"C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe" -out .\obj .\src\Product.wxs .\src\Extension.wxs

However, when I need to customize the installer with client specific files, I simply change the command line such that it points to the customized Extension.wxs file (sort of 'injecting' it into the build pipeline):

"C:\Program Files (x86)\WiX Toolset v3.11\bin\candle.exe" -out .\obj .\src\Product.wxs .\SomeClientWithSpecialNeeds\Extension.wxs

Customizing these command line invocations becomes much easier when using Powershell, but I've omitted the Powershell scripts for the sake of simplicity.

In summary, I'm suggesting that you represent the contents of your zip file as individual Component elements in a separate Extension.wxs file that you inject into your installer's build pipeline. These components are stored in a ComponentGroup that is referenced by the appropriate Feature. By creating a suitable default (empty, devoid of components) Extension.wxs file you can still perform a build without any custom components. The other benefit of using this pattern, is that all of the extra components will be uninstalled during uninstallation of the product, and you won't have to write any flaky custom actions that add to maintenance overhead for your installer!

Hope this helps someone.

pixelTitan
  • 455
  • 3
  • 10
  • 1
    Very nice write-up, somewhat similar to pre-processor use. I have added an answer on how to use **pre-processor variables**. Not sure which approach is easiest, but I think pre-processor use is more flexible when it comes to build processes. – Stein Åsmul Jun 19 '18 at 15:34
  • Thanks @SteinÅsmul, I definitely think the pre-processor methodology is more flexible across the various WiX Toolset build techniques (i.e. Visual Studio vs CLI). In my situation I went with the "injection" approach because I've always liked the concept that a standard installation code base (or executable code base, for that matter) could be decoupled from the custom implementations. If the user is OK with stronger coupling between the standard code base and the custom ones, I definitely think your answer is the way to go. – pixelTitan Jun 19 '18 at 15:51
0

Pre-Processor Constructs: If you need to deliver flavours of your setup to different customers, I like to use pre-processor constructs to compile an MSI for each client. Here is a very lenghty description of this approach (maybe check the code snippets towards the bottom).

Pre-Processor Constructs In Use: I'll inline the code / markup from the above, linked answer with some tweaks to hopefully make it clearer (I find this a very "coder friendly" approach - this is very familiar territory for C++ developers in particular - to use pre-processor constructs that is):

<?define ClientName = “Apple” ?>

<...>

<!-- You can put your vendor-specific components in an include file  -->
  <?if $(var.ClientName ) = "Apple" ?> 
    <?include "AppleFeatures.wxi" ?>
  <?endif ?>
  <?if $(var.ClientName ) = "Microsoft" ?>
    <?include "MicrosoftFeatures.wxi" ?>
  <?endif ?>

<...>

Essentially pre-processor constructs allow you to change the WiX source file on the fly before it is being compiled and linked, so you can include or exclude certain sections at will. You can do this by setting a few paramenters - for example the client name - which you can then use to output a specific MSI file name: YourProduct_AppleEdition.msi or YourProduct_SonyEdition.msi, etc...

Here is a quick description of the differences between localization variables, pre-processor variables and include files: WiX (Windows Installer Xml), Create universal variables.

Stein Åsmul
  • 39,960
  • 25
  • 91
  • 164