2

Complete WiX newbie has been given a task to do and not sure how to tackle it or if it is even possible. What I have are two executables that will live in their own folders under the vendor directory like so:

C:\Program Files (x86)\My Company
 |
 +---Program A
 |    |
 |    +---config.txt
 |    +---program_A.exe
 |
 +---Program B
 |    |
 |    +---config.txt
 |    +---program_B.exe

Program A and Program B will always be packaged together and are both installed as windows services. Program A will likely update more frequently than Program B and when this happens, I need Program B to keep running as it is reporting the status of Program A back to another machine.

What I have managed to achieve is having two components in the same WiX file but obviously there was only one upgrade code in the product element so they both uninstall and then reinstall even though there was no change to Program B.

I tried the route of chaining (using burn) two seperate MSI's but I was told we can only distribute the product as MSI and not exe like burn produces.

Is it possible to achieve what I want and if so, what do i need to research to do it?

Edit I tried creating two modules and using a merge but this didn't seem to work either. Here is one of my modules:

MergeModule1.wxs

<?xml version='1.0'?>

<?define version = "!(bind.fileVersion.Program1_Exe)"?>
<?define language = 1033?>
<?define company_name = "ABC Soft"?>
<?define codename = program1?>
<?define product_name = Program1?>
<?define full_product_name = '$(var.company_name) $(var.product_name)'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'
 xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">

<Module Id="MergeModule1ID" Language='$(var.language)' Version='$(var.version)'>

    <Package Id="3a281d57-33fd-4601-8aee-9e6349701b4b"
        Description='$(var.full_product_name)'
        Manufacturer='$(var.company_name)'
        InstallScope='perMachine'
        InstallerVersion='200'
        Comments='Version: $(var.version)'>
    </Package>

    <Directory Id='TARGETDIR' Name='SourceDir'>
        <Directory Id='ProgramFilesFolder' Name='PFiles'>
            <Directory Id='VendorDir' Name='$(var.company_name)'>
                <Directory Id='TheProgramDir' Name='$(var.product_name)'/>
            </Directory>
        </Directory>
    </Directory>

    <DirectoryRef Id="TheProgramDir">
        <Component Id='Program1_Exe' Guid='d6fdaf52-16aa-42f8-9790-4287510f53f5'>
            <File Id='Program1_Exe'
                Name='Program1.exe'
                Source='Program1.exe'
                KeyPath='yes' />

            <ServiceInstall Id='InstallService'
                Name='$(var.codename)'
                DisplayName='$(var.full_product_name)'
                Description='$(var.full_product_name)'
                Type='ownProcess'
                Vital='yes'
                Start='auto'
                ErrorControl='ignore'
                Interactive='no'>   

                <util:ServiceConfig
                    FirstFailureActionType="restart"
                    SecondFailureActionType="restart"
                    ThirdFailureActionType="restart"
                    ResetPeriodInDays="1"
                    RestartServiceDelayInSeconds="60" />
            </ServiceInstall>

            <ServiceControl Id='StartService'
                Start='install'
                Stop='both'
                Remove='uninstall'
                Wait='yes'
                Name='$(var.codename)' />
        </Component>
    </DirectoryRef>

</Module>

I also have a MergeModule2.wxs with the relevant changes for Program2 exe and different GUID. Program1 exe has version 2.0.0.1 whereas Program2 exe is on 1.0.2.1 so the version numbers in the modules are set accordingly. In the merge wix file I have:

MergeInstallers.wxs

<?xml version='1.0' encoding='windows-1252'?>

<?define version = "0.1.0.0"?>
<?define language = 1033?>
<?define company_name = "ABC Soft"?>
<?define codename = program?>
<?define product_name = Program?>
<?define full_product_name = '$(var.company_name) $(var.product_name)'?>
<?define upgrade_code = eaf3891c-cd16-4239-b8bc-1b2cd7816c16?>

<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi'>

<Product Id='*'
  UpgradeCode='$(var.upgrade_code)'
  Name='$(var.full_product_name)'  
  Language='$(var.language)'
  Version='$(var.version)' 
  Manufacturer='$(var.company_name)'>

  <Package Description='$(var.full_product_name)'
      Manufacturer='$(var.company_name)'
      InstallScope='perMachine'
      InstallerVersion='200'
      Compressed='yes'
      Comments='Version: $(var.version)'>
  </Package>  

  <Upgrade Id='$(var.upgrade_code)'>

      <UpgradeVersion OnlyDetect='no' Property='NEWPRODUCTFOUND'
          Minimum='$(var.version)' IncludeMinimum='no'
          Language='$(var.language)' />
      <UpgradeVersion OnlyDetect='no' Property='OLDPRODUCTFOUND'
          Maximum='$(var.version)' IncludeMaximum='no'
          Language='$(var.language)' />
      <UpgradeVersion OnlyDetect='no' Property='CURRENTPRODUCTFOUND'
          Minimum='$(var.version)' IncludeMinimum='yes'
          Maximum='$(var.version)' IncludeMaximum='yes'
          Language='$(var.language)' />
  </Upgrade>

  <Media Id='1' Cabinet='$(var.codename).cab' EmbedCab='yes'></Media>

  <Directory Id='TARGETDIR' Name='SourceDir'>
      <Directory Id='ProgramFilesFolder' Name='PFiles'>
          <Directory Id='VendorDir' Name='$(var.company_name)'>
              <Merge Id="Mod1" Language='$(var.language)' SourceFile="Program1\MergeModule1.msm" DiskId="1" />
              <Merge Id="Mod2" Language='$(var.language)' SourceFile="Program2\MergeModule2.msm" DiskId="1" />
          </Directory>
      </Directory>
  </Directory>

  <Feature Id="Msm" Title="Msm" Level="1">
      <MergeRef Id="Mod1" />
      <MergeRef Id="Mod2" />
  </Feature>

  <InstallExecuteSequence>
      <RemoveExistingProducts After="InstallFinalize" />
  </InstallExecuteSequence>

</Product>

Now, on the first install of the msi, two services are installed and running fine. I now make a change to Program1.exe so that the version of the exe changes from 2.0.0.1 to 2.0.0.2 and Program2.exe remains unchanged at 1.0.2.1 (and subsequently, the module version for Program1 will change too). I rebuilt everything and ran the msi and both programs where reinstalled and both services restarted. What I expected to happen is that Program1 would be replaced due to version change and Program2 will continue to run without hesitation. Am I missing something obvious or am I way off in my logic?

incubus
  • 681
  • 1
  • 5
  • 21
  • By adjusting the position of the [`RemoveExistingProducts`](https://learn.microsoft.com/en-us/windows/desktop/msi/removeexistingproducts-action) action in the `InstallExecuteSequence`, you can make it so that the new version will be installed, before the old version will be removed. A component that has the same GUID in both old and new version, won't be removed. Possible sequencing is described in detail under the above link. Understanding [component rules](http://robmensching.com/blog/posts/2003/10/18/component-rules-101/) will be crucial for succeeding with this solution. – zett42 Mar 01 '19 at 21:02
  • Just curious about this design. What do these services do? And do they share many files and that is why you want one setup? In addition to zett42's advice: [you can use a merge module to include the same files in two different setups](https://stackoverflow.com/a/49078046/129130) - that should work - with full reference counting (files get uninstalled when last product is removed). Depends on your requirements. I have [this old piece on splitting setups](https://stackoverflow.com/a/1546916/129130). Maybe give it a skim? – Stein Åsmul Mar 02 '19 at 15:36
  • My open source tool and tutorials IsWiX makes this super easy. 99% of the installer code is authored automatically with only one attribute being required to get the behavior desired in this situation. – Christopher Painter Mar 02 '19 at 16:42

1 Answers1

1

This is super easy. Start with this tutorial:

https://github.com/iswix-llc/iswix-tutorials

Create two service projects with two postbuild copies and define two services in the merge module.

Finally in Product.wxs's MajorUpgrade element add Schedule="afterInstallExecute" to it.

http://wixtoolset.org/documentation/manual/v3/xsd/wix/majorupgrade.html

Here's the important part. You have to make sure you version and build your services 100% correctly. Only include a newer EXE when it has actually changed. If the version hasn't changed MSI won't reinstall the component so the service won't get stopped ad started. If the version has changed it'll upgrade the component resulting in the service being stopped and started as part of the upgrade.

Christopher Painter
  • 54,556
  • 6
  • 63
  • 100
  • Christopher, I have added my example to the original question. I don't use Visual Studio for generating the projects (opting for VIM instead :) ) but I don't think that should make much difference other than making it harder for myself. Is there anything obvious missing? – incubus Mar 05 '19 at 21:24
  • Sorry, I only do things the easy way. :) – Christopher Painter Mar 06 '19 at 02:37
  • I tried to follow this but it's pretty convoluted. :( – Phill May 04 '23 at 07:59