Search code examples
wixwindows-installer

WIX MSI Installing driver from DLL library in directory created by MSI causes issues during upgrade


1. Introduction

I've got an MSI installer build via the WIX toolset. This installer contains a dll library and a *.cab file that is used to install a 3rd party driver while installing my application. This process is done in the following steps: my MSI creates a directory in the INSTALLDIR, then the driver is installed there by executing a deferred custom action from mentioned dll. I think that this pattern isn't really a valid one, but that's a side note.

During an upgrade, the directory where MSI installed a driver is removed. This causes issues when it's time to upgrade the driver as it must be done via another custom action defined in the dll library and those removed files must be present. Please notice: I cannot uninstall and install the driver during an upgrade, it's a limitation, unfortunately.

2. Workaround

The RemoveExistingProduct was scheduled after InstallInitialize. As the driver's files mustn't be touched during an upgrade, as a workaround, I've changed RemoveExistingProduct to be executed after InstallExecute, so the files aren't firstly removed and then installed again, but rather overwritten if needed. I'm aware of how this affects the rollout procedures.

Question: Is this a proper/better way (as proper as a workaround can be...) of handling it? Can it cause some unwanted side-effects? So far I've observed in logs:

Disallowing uninstallation of component: {GUID-HERE} since another client exists

Another question: Is this expected?

3. Details and some XML

The Product ID and Package ID are always generated ("*"). The UpgradeCode remains the same between different versions. The REINSTALLMODE="omus". The upgrade is done via <Upgrade> element:

<Upgrade Id="$(var.UpgradeCode)">
  <UpgradeVersion OnlyDetect='no' Property='AUTO_FOUND_PREVIOUS'
    Maximum='$(var.VersionNumber)' IncludeMaximum='no'
    IgnoreRemoveFailure="yes" MigrateFeatures="yes" />

  <UpgradeVersion OnlyDetect='no' Property='AUTO_FOUND_SELF'
   Minimum='$(var.VersionNumber)' IncludeMinimum='yes'
   Maximum='$(var.VersionNumber)' IncludeMaximum='yes'
   IgnoreRemoveFailure="yes" MigrateFeatures="yes" />

  <UpgradeVersion OnlyDetect='yes' Property='AUTO_FOUND_NEWER'
   Minimum='$(var.VersionNumber)' IncludeMinimum='no' />
</Upgrade>

And as I mentioned, the directory where the driver is installed is also managed by my MSI:

<DirectoryRef Id='INSTALLDIR_DRIVER'>
    <Component Id='cmp_driverPlaceholderDir' Guid='{CONST-GUID-HERE}'>
        <CreateFolder />
        <RemoveFolder Id='INSTALLDIR_DRIVER' On='uninstall' />
    </Component>
</DirectoryRef>

Other components have also constant GUIDs.

I'd love to also hear how the installation of an external driver from custom action should be handled in the proper way, don't hesitate if you have some knowledge about it.


Solution

  • At one time DIFX was the "authoritative method" for driver installations using MSI, but it has now been deprecated by Microsoft. The recommended replacement is called Setup API or Device and Driver Installation Reference (depending on much platform support you need) but each evolution is more difficult to use within the confines of Windows Installer (although I've been tempted several times to write a WiX extension wrapping those APIs to help all the driver writers out there) because of the need to maintain MSI's transactional guarantees whenever using deferred custom actions.

    Current best practice (early 2021) is to use a bootstrapper that supports multiple packages & will securely cache your packages (so they are available during upgrades & uninstalls, etc.), and to have it install both your driver and your MSI.