2
votes

I have two components in which one component have simple.exe and another component has sample.dll in one MSI. If i install MSI both simple.exe and sample.dll will install and in program files under some folder it is having two one is .exe and .dll. If I do a major upgrade to entire MSI, it will replace the folder that is created in the program files with new one. So how to make wix not replace .exe if i change / provide update for .dll file and vice versa. And while providing an update to .dll I will register and unregister keys using some .bat file. Please help me to solve I need a conditional major upgrade that will replace particular files when I change them.

Please find the below code:

product.wxs

<?xml version="1.0" encoding="UTF-8"?>
<?define ProductVersion = "0.0.4"?>
<?define ProductUpgradeCode = "d3170abf-b41c-4274-a3a0-85576052f35c"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  <Product Id="*" Name="saranSample" Language="1033" Version="$(var.ProductVersion)" Manufacturer="example" UpgradeCode="$(var.ProductUpgradeCode)">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <MajorUpgrade DowngradeErrorMessage="A newer version of product is already installed." AllowSameVersionUpgrades="no" AllowDowngrades="no" />
    <MediaTemplate EmbedCab="yes" />

<Upgrade Id="$(var.ProductUpgradeCode)">
  <UpgradeVersion Minimum="$(var.ProductVersion)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
  <UpgradeVersion Minimum="0.0.0" Maximum="$(var.ProductVersion)" IncludeMinimum="yes" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>
<InstallExecuteSequence>
  <Custom Action="Filecleaner" After="InstallFinalize"></Custom>
</InstallExecuteSequence>
<Condition Message="A newer version of this software is already installed.">NOT NEWERVERSIONDETECTED</Condition>

    <Directory Id="TARGETDIR" Name="SourceDir">
        <Directory Id="ProgramFilesFolder">
    <Directory Id="INSTALLFOLDER" Name="saranSample_$(var.ProductVersion)">
      <Component Id="exeFiles" Guid="12345678-1234-1234-1234-222222222222">
        <File Id="exe" Source="$(sys.CURRENTDIR)npp.7.5.7.Installer.exe" KeyPath="yes"/>
      </Component>
      <Component Id="dllFiles" Guid="12345678-1234-1234-1234-222222222223">
        <File Id="dll" Source="$(sys.CURRENTDIR)saran.dll" KeyPath="yes"/>
      </Component>
    </Directory>
        </Directory>
    </Directory>

<Feature Id="ProductFeature" Title="saranSample" Level="1">
  <ComponentRef Id="exeFiles"/>
  <ComponentRef Id="dllFiles"/>
</Feature>

2

2 Answers

1
votes

I second all Phil's points, and maybe I can add a couple of things:

One File Per Component: Make sure you use a single component per file you install. I do this for all file types, but you have to for binaries (dlls and exes). This is to make the installer operate correctly and be in accordance with MSI component rules (mentioned by Phil). I see you have a component EXE files and one for DLL files. Make new components for each binary you add to your setup, and set the binary as key path for the component.


Selective File Update: If what you want to do is keep older versions of files on disk whilst installing a new package with higher version files in a selective fashion, then I don't know of a way to do this short of trying to set the files read-only (which I have never tried - not sure what will happen to be honest). You can also set a blank component GUID (such a GUID is also known as a "Little Bobby Void-GUID", just check this very official source) - this will install one or more files and never touch or overwrite them again. There is also a component flag that can be set to never overwrite if the component key path exists (I usually combine this with setting the component permanent). It is also possible to fake a version number for a file by post-processing the MSI you build. Some third party tools such as Installshield and Advanced Installer provides a GUI to do so before compilation.

MSI will generally overwrite only lower version files in the target location with higher version binaries in the setup (this is according to the default file versioning rules, which can be affected and modified by setting a custom value for REINSTALLMODE - a "modifier" for the file overwrite rules). If the files in the target destination are the same or higher versions than those being installed, then the targets will not be overwritten (unless REINSTALLMODE is set to amus - force overwrite - which is a horrible concept and has many side-effects).

In case it is not obvious: if some of your files are not updated for a given release, then you just include the previous versions of those files that were included in the older setup, and they will show up on disk along with the files you have new versions for. There is nothing you have to do to make this happen, it will be auto-magical.


Late-Sequenced Major Upgrade: Phil's point number 3 is about late sequencing of major upgrade. Instead of uninstalling all files, and then reinstalling them, such a major upgrade effectively installs as a patch comparing differences between the old and the new package and removing only files that are deleted in the most recent package. All other files are preserved on disk or updated as appropriate based on the file overwrite rules.


Preserve Settings Files: If you have settings files that you want to preserve between releases, the typical problem is that a major upgrade will not overwrite changed, non-versioned files (file overwrite rules will not overwrite changed settings files), but it will happily uninstall and reinstall them unless they are marked permanent in the original setup - and then reinstall them in their original state making them appear overwritten (when they are actually uninstalled and reinstalled - or reverted if you like). It is a technology anti-pattern in my opinion.

The above mentioned late-sequenced major upgrade feature / approach prevents this settings file reversion problem because files are not summarily uninstalled and reinstalled, but compared between setup versions and left untouched if they are settings files that have changes - that is provided you have implemented everything correctly according to MSI component rules, and this is no picnic to get right. You need 100% component rules compliance to avoid other side-effects such as missing files after update is complete for example. A short piece on the component rules.

Alternatively, you can set the settings files permanent in your MSI. Then they will never be uninstalled, but also likely never updated and never uninstalled unless you create your own constructs to do so.


Path Variables: Does $(sys.CURRENTDIR) work well for you? I like to define myself my own path variable that I can re-yank to whichever folder I want to with ease:

<?define SourceFiles= "C:\Projects\MySetup\Releases\1.0.0\"?>

<...>

<Icon Id="Icon.exe" SourceFile="$(var.SourceFiles)\MyApp.exe" />

Some Links:

0
votes

A few things:

  1. Why do you have a MajorUpgrade element as well as Upgrade elements? The MajorUpgrade element should be all that you need, so it is confusing things.

  2. As in the MajorUpgrade documentation, the default sequencing for the upgrade is afterInstallValidate. This is "early" in the upgrade and effectively uninstalls all the older product before installing the new version. That's why the files are removed (by the uninstall) and then installed again.

  3. It seems that you need your MajorUpgrade Schedule to be afterInstallExecute. This installs the new product files over the older files, using file version overwrite rules. If the file versions of the binary files have not changed then they will not be replaced. This type of upgrader also requires that you follow component rules:

http://robmensching.com/blog/posts/2003/10/18/component-rules-101/

https://docs.microsoft.com/en-us/windows/desktop/msi/what-happens-if-the-component-rules-are-broken

Also, you mention registration using a bat file, and this is not required. WiX has the Heat tool for extracting registration at build ti,e.