Make WiX installation set upgrade to the same folder - wix

How can I make a major upgrade to an installation set (MSI) built with WiX install into the same folder as the original installation?
The installation is correctly detected as an upgrade, but the directory selection screen is still shown and with the default value (not necessarily the current installation folder).
Do I have to do manual work like saving the installation folder in a registry key upon first installing and then read this key upon upgrade? If so, is there any example?
Or is there some easier way to achieve this in MSI or WiX?
As reference, I my current WiX file is below:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">
<Product Id="a2298d1d-ba60-4c4d-92e3-a77413f54a53"
Name="MyCompany Integration Framework 1.0.0"
Language="1033"
Version="1.0.0"
Manufacturer="MyCompany"
UpgradeCode="9071eacc-9b5a-48e3-bb90-8064d2b2c45d">
<!-- Package information -->
<Package Keywords="Installer"
Id="e85e6190-1cd4-49f5-8924-9da5fcb8aee8"
Description="Installs MyCompany Integration Framework 1.0.0"
Comments="Installs MyCompany Integration Framework 1.0.0"
InstallerVersion="100"
Compressed="yes" />
<Upgrade Id='9071eacc-9b5a-48e3-bb90-8064d2b2c45d'>
<UpgradeVersion Property="PATCHFOUND"
OnlyDetect="no"
Minimum="0.0.1"
IncludeMinimum="yes"
Maximum="1.0.0"
IncludeMaximum="yes"/>
</Upgrade>
<!-- Useless but necessary... -->
<Media Id="1" Cabinet="MyCompany.cab" EmbedCab="yes" />
<!-- Precondition: .NET 2 must be installed -->
<Condition Message='This setup requires the .NET Framework 2 or higher.'>
<![CDATA[MsiNetAssemblySupport >= "2.0.50727"]]>
</Condition>
<Directory Id="TARGETDIR"
Name="SourceDir">
<Directory Id="MyCompany"
Name="MyCompany">
<Directory Id="INSTALLDIR"
Name="Integrat"
LongName="MyCompany Integration Framework">
<Component Id="MyCompanyDllComponent"
Guid="4f362043-03a0-472d-a84f-896522ce7d2b"
DiskId="1">
<File Id="MyCompanyIntegrationDll"
Name="IbIntegr.dll"
src="..\Build\MyCompany.Integration.dll"
Vital="yes"
LongName="MyCompany.Integration.dll" />
<File Id="MyCompanyServiceModelDll"
Name="IbSerMod.dll"
src="..\Build\MyCompany.ServiceModel.dll"
Vital="yes"
LongName="MyCompany.ServiceModel.dll" />
</Component>
<!-- More components -->
</Directory>
</Directory>
</Directory>
<Feature Id="MyCompanyProductFeature"
Title='MyCompany Integration Framework'
Description='The complete package'
Display='expand'
Level="1"
InstallDefault='local'
ConfigurableDirectory="INSTALLDIR">
<ComponentRef Id="MyCompanyDllComponent" />
</Feature>
<!-- Task scheduler application. It has to be used as a property -->
<Property Id="finaltaskexe"
Value="MyCompany.Integration.Host.exe" />
<Property Id="WIXUI_INSTALLDIR"
Value="INSTALLDIR" />
<InstallExecuteSequence>
<!-- command must be executed: MyCompany.Integration.Host.exe /INITIALCONFIG parameters.xml -->
<Custom Action='PropertyAssign'
After='InstallFinalize'>NOT Installed AND NOT PATCHFOUND</Custom>
<Custom Action='LaunchFile'
After='InstallFinalize'>NOT Installed AND NOT PATCHFOUND</Custom>
<RemoveExistingProducts Before='CostInitialize' />
</InstallExecuteSequence>
<!-- execute comand -->
<CustomAction Id='PropertyAssign'
Property='PathProperty'
Value='[INSTALLDIR][finaltaskexe]' />
<CustomAction Id='LaunchFile'
Property='PathProperty'
ExeCommand='/INITIALCONFIG "[INSTALLDIR]parameters.xml"'
Return='asyncNoWait' />
<!-- User interface information -->
<UIRef Id="WixUI_InstallDir" />
<UIRef Id="WixUI_ErrorProgressText" />
</Product>
</Wix>

There's an example in the WiX tutorial: https://www.firegiant.com/wix/tutorial/getting-started/where-to-install/
<Property Id="INSTALLDIR">
<RegistrySearch Id='AcmeFoobarRegistry' Type='raw'
Root='HKLM' Key='Software\Acme\Foobar 1.0' Name='InstallDir' />
</Property>
Of course, you've got to set the registry key as part of the install too. Stick this inside a component that's part of the original install:
<RegistryKey
Key="Software\Software\Acme\Foobar 1.0"
Root="HKLM">
<RegistryValue Id="FoobarRegInstallDir"
Type="string"
Name="InstallDir"
Value="[INSTALLDIR]" />
</RegistryKey>

'Registry' is deprecated. Now that part of code should look like this:
<RegistryKey Id="FoobarRegRoot"
Action="createAndRemoveOnUninstall"
Key="Software\Software\Acme\Foobar 1.0"
Root="HKLM">
<RegistryValue Id="FoobarRegInstallDir"
Type="string"
Name="InstallDir"
Value="[INSTALLDIR]" />
</RegistryKey>

You don't really need to separate RegistryKey from RegistryValue in a simple case like this. Also, using HKMU instead of HKLM takes care of it whether you're doing a machine or user install.
<RegistryValue
Root="HKMU"
Key="Software\[Manufacturer]\[ProductName]"
Name="InstallDir"
Type="string"
Value="[INSTALLDIR]"
KeyPath="yes" />

Related

Wix remove appdata folder on uninstall

I am using Wix 3.11 and have tried a number of different ways and methods and can't seem to be able to figure out why Wix isn't removing my ApplicationDataFolder folder. During install I create the folder in the ApplicationDataFolder location for the user.
My application uses logging and stores it along with a few other things in the directory. During uninstall everything in the application folder is removed but the appdata folder with the logs and other files are left untouched.
I can't figure out why or what I am missing.
WXS Template
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Id="*" UpgradeCode="9e578e3d-0119-425c-8633-f54ffaaa4929" Name="#product.name#" Version="#product.version#" Manufacturer="#product.company#" Language="1033">
<Package InstallerVersion="400" Compressed="yes" InstallScope="perMachine" Comments="#product.version#" Description="#product.description#"/>
<Media Id="1" Cabinet="SomeApp.cab" EmbedCab="yes" />
<!-- Installer Properties -->
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
<PropertyRef Id="WIX_IS_NETFRAMEWORK_46_OR_LATER_INSTALLED"/>
<!-- Installer Resources -->
<Icon Id="ApplicationIcon" SourceFile="SomeApp 4\SomeApp 4_vista.ico"/>
<Property Id="ARPPRODUCTICON" Value="ApplicationIcon" />
<Property Id="INSTALLDIR">
<RegistrySearch Key="SOFTWARE\Acme\SomeApp"
Root="HKCU" Type="raw"
Id="APPLICATIONFOLDER_REGSEARCH" Name="installation-path" />
</Property>
<WixVariable Id="WixUILicenseRtf" Value="SomeApp 4\license.rtf" />
<WixVariable Id="WixUIBannerBmp" Value="WixUIBannerBmp.bmp" />
<WixVariable Id="WixUIDialogBmp" Value="WixUIDialogBmp.bmp" />
<!-- Check Existing Install -->
<Upgrade Id="9e578e3d-0119-425c-8633-f54ffaaa4929">
<UpgradeVersion Minimum="#product.version#" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
<UpgradeVersion Minimum="0.0.0" Maximum="#product.version#" IncludeMinimum="yes" IncludeMaximum="no" Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>
<Condition Message="A newer version of this software is already installed.">NOT NEWERVERSIONDETECTED</Condition>
<!-- Prerequisites -->
<Condition Message="This application requires .NET Framework 4.6 or newer. Please install the .NET Framework then run this installer again.">
<![CDATA[Installed OR WIX_IS_NETFRAMEWORK_46_OR_LATER_INSTALLED]]>
</Condition>
<Condition Message="This application requires at least Windows 7 or Windows Server 2008 R2. Please upgrade your computer to a supported operating system and run this installer again.">
<![CDATA[Installed OR (VersionNT >= 601)]]>
</Condition>
<!-- Define the directory structure -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="ProgramFiles">
<Directory Id="INSTALLDIR" Name="#product.company#">
#product.applicationfiles#
#product.servicefiles#
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="#product.name#"/>
<Directory Id="ServiceProgramsFolder" Name="#product.name#"/>
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="LocalAppDataFolder">
<Directory Id="ApplicationDataFolder" Name="Acme" />
</Directory>
</Directory>
<DirectoryRef Id="ApplicationProgramsFolder">
<Component Id="ApplicationShortcut" Guid="1e578e4d-0229-425c-8633-f54ffaaa4901">
<Shortcut Id="ApplicationStartMenuShortcut"
Name="SomeApp 4.6"
Description="#product.company# #product.name# #product.version#"
Target="[INSTALLDIR]SomeApp 4\SomeApp.UserInterface.exe"
WorkingDirectory="INSTALLDIR"
Icon ="ApplicationIcon"/>
<Shortcut Id="ApplicationStartMenuShortcut2"
Name="SomeApp 4.6 (Multiple Instances)"
Description="#product.company# #product.name# #product.version#"
Target="[INSTALLDIR]SomeApp 4\SomeApp.UserInterface.exe"
Arguments="MultipleInstance=True"
WorkingDirectory="INSTALLDIR"
Icon ="ApplicationIcon"/>
<Shortcut Id="ApplicationStartMenuShortcut3"
Name="SomeApp 4.6 (Notifications)"
Description="#product.company# #product.name# #product.version#"
Target="[INSTALLDIR]SomeApp 4\SomeApp.UserInterface.exe"
Arguments="Notifications=True"
WorkingDirectory="INSTALLDIR"
Icon ="ApplicationIcon"/>
<Shortcut Id="HelpStartMenuShortcut"
Name="SomeApp 4.6 Help"
Target="[INSTALLDIR]SomeApp 4\Documentation\SomeApp.chm"
WorkingDirectory="INSTALLDIR"/>
<Shortcut Id="UninstallProduct"
Name="Uninstall SomeApp 4.6"
Target="[SystemFolder]msiexec.exe"
Arguments="/x [ProductCode]"
Description="Uninstall #product" />
<Shortcut Id="desktopshortcut"
Directory="DesktopFolder"
Name="SomeApp 4.6"
WorkingDirectory="INSTALLDIR"
Target="[INSTALLDIR]SomeApp 4\SomeApp.UserInterface.exe" />
<RemoveFolder Id="ApplicationProgramsFolder" On="uninstall"/>
<util:RemoveFolderEx On="uninstall" Property="ApplicationDataFolder" />
</Component>
</DirectoryRef>
<DirectoryRef Id="ServiceProgramsFolder">
<Component Id="ServiceShortcut" Guid="9e578e3d-0229-425c-8633-f54ffaaa4901">
<Shortcut Id="ServiceStartMenuShortcut"
Name="#product.name# Reporting Service"
Description="#product.name# Reporting Service"
Target="[INSTALLDIR]Reporting\SomeApp.ReportingService.exe"
WorkingDirectory="INSTALLDIR"
Icon ="ApplicationIcon"/>
<RemoveFolder Id="ServiceProgramsFolder" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\Microsoft\Acme\SomeApp" Name="service-installed" Type="integer" Value="1" KeyPath="yes"/>
</Component>
</DirectoryRef>
<DirectoryRef Id="INSTALLDIR">
<Component Id="CleanupMainApplicationFolder" Guid="*">
<RegistryValue Root="HKCU" Key="SOFTWARE\Acme\SomeApp" Name="installation-path" Type="string" Value="[INSTALLDIR]" KeyPath="yes" />
<util:RemoveFolderEx On="uninstall" Property="INSTALLDIR" />
<util:RemoveFolderEx On="uninstall" Property="ApplicationDataFolder" />
</Component>
</DirectoryRef>
<!-- Feature: SomeApp Application -->
<Feature Id="Feature.Application"
Title="SomeApp 4 - Application"
Description="SomeApp is an asset management and maintenance application designed to optimize asset value and improve manufacturing productivity."
ConfigurableDirectory="INSTALLDIR"
Level="1"
AllowAdvertise="no">
#product.applicationcomponents#
<ComponentRef Id="ApplicationShortcut" />
<ComponentRef Id="CleanupMainApplicationFolder" />
</Feature>
<!-- Feature: Reporting Service -->
<Feature Id="Feature.Service"
Title="SomeApp 4 - Reporting Service"
Description="This service generates and delivers reports that have been scheduled in the SomeApp Maintenance Management System."
ConfigurableDirectory="INSTALLDIR"
Level="3"
AllowAdvertise="no">
#product.servicecomponents#
<ComponentRef Id="ServiceShortcut" />
<Component Id="ReportingServiceInstaller" Guid="B72CAA3F-F2DB-48D2-90DD-061209AB2CE5" Directory="INSTALLDIR">
<CreateFolder />
<File Id="ReportingService.exe" Name="ReportingService.exe" KeyPath="yes" Source="#product.sourcedir#\Reporting\SomeApp.ReportingService.exe"/>
<ServiceInstall Id="ReportingServiceInstaller"
Type="ownProcess"
Vital="yes"
Name="SomeApp Reporting Service"
DisplayName="SomeApp - Reporting Service"
Description="This service generates and delivers reports that have been scheduled in the SomeApp Maintenance Management System."
Start="auto"
Account="NT AUTHORITY\LocalService"
ErrorControl="ignore"
Interactive="no" />
</Component>
</Feature>
<CustomAction Id="Cleanup_logfile" Directory="INSTALLDIR"
ExeCommand="cmd /C "rmdir %LOCALAPPDATA%\Acme /s /q""
Execute="deferred" Return="ignore" HideTarget="no" Impersonate="no" />
<InstallExecuteSequence>
<Custom Action="Cleanup_logfile" After="RemoveFiles" >
REMOVE="ALL"
</Custom>
</InstallExecuteSequence>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallValidate"/>
</InstallExecuteSequence>
<UIRef Id="WixUI_FeatureTree" />
<UI>
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<!-- Add the GUI logic for installation -->
</UI>
</Product>
</Wix>
I have one project that uses util:RemoveFolderEx where it is working properly. The only difference I can see is that I explicitly set a property at runtime to the value of the install directory. My guess is that "INSTALLDIR" and "ApplicationDataFolder" are not actually properties since they are stored in the Directory table of your msi and not the property table. (Checked using ORCA)
Try doing
<SetProperty Id='AcmeAppDataFolderToRemove' Value='[ApplicationDataFolder]' After='AppSearch'/>
and then change your util:RemoveFolderEx to
<util:RemoveFolderEx On="uninstall" Property="AcmeAppDataFolderToRemove" />
and see if that works. (You might need to use Value='ApplicationDataFolder' without the []'s not sure.)
In response to your edit I went and looked at the RemoveFolderEx page and this looks important
The custom action that implements RemoveFolderEx does so by writing temporary rows to the RemoveFile table for each subfolder of the root folder you specify. Because it might dramatically affect Windows Installer's File Costing, the temporary rows must be written before the CostInitialize standard action. Unfortunately, MSI doesn't create properties for the Directory hierarchy in your package until later, in the CostFinalize action.
I figured setting it to the directory would work but apparently these directories don't have values yet when RemoveFolderEx action actually executes. So, when this tries to resolve the properties or set the properties they are actually just empty.
A difference in what I said my installer does and what it actually does is that I get the value I set the property to through a RegistrySearch.
<Property Id="INSTALLDIR">
<RegistrySearch
Id='InstallDirRegistrySearch'
Type='raw'
Root='HKLM'
Key='SOFTWARE\$(var.OEMRegistryRootKeyName)'
Name='SDKPath' />
</Property>
<SetProperty Id='SDKFolderToRemove' Value='[INSTALLDIR]\$(var.OEMProduct) SDK' After='AppSearch'/>
So I would try writing the appdata dir to a registry key and getting the value into a property via registry search then using that property in your removefolderex, you probably don't need the setproperty, I just used it because I needed a folder under INSTALLDIR but in your case you can just have the registry key be the right folder.

WiX: Setting the install directory from a RegistySearch causes the error "Could not acess network location"

I am making an installer based on WixUI_Advanced. I am using the solution from here: WiX: How to override "C:\Program Files (x86)" on x64 machine in WixUI_Advanced sequence?
However, I want the installer to remember the previous install directory. Attempting to set the APPLICATIONFOLDER property results in the error "Could not access network location" followed by the directory name. This only happens when a value is actually found in the registry (when upgrading the program).
Here is the relevant code:
<Product Id="*" Name="$(var.ProductName)" Language="1033" Version="$(var.Version)" Manufacturer="OronDF343" UpgradeCode="22187c5e-5fd6-4734-802e-236abd321433">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Description="$(var.ProductName)" />
<PropertyRef Id="NETFRAMEWORK45" />
<Condition Message="This application requires .NET Framework 4.5.1 or later. Please install .NET Framework version 4.5.1 or higher and then run this installer again.">
<![CDATA[Installed OR (NETFRAMEWORK45 >= "#378758")]]>
</Condition>
<MajorUpgrade AllowSameVersionUpgrades="yes" DowngradeErrorMessage="A newer version of $(var.GlobalProductName) is already installed." />
<!-- This is where the problem is. This is how it was implemented in the solution linked above, but I have tried it without the SetDirectory. -->
<Property Id="APPLICATIONFOLDER" Secure="yes">
<RegistrySearch Id="RegistrySearch" Type="raw" Root="HKLM" Win64="$(var.Win64)" Key="Software\$(var.Company)\$(var.ProgID)" Name="InstallLocation" />
</Property>
<SetDirectory Id="APPLICATIONFOLDER" Value="[$(var.PlatformProgramFilesFolder)]$(var.GlobalProductName)">APPLICATIONFOLDER=""</SetDirectory>
<!---->
<MediaTemplate EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" Name="Desktop" />
<Directory Id="$(var.PlatformProgramFilesFolder)">
<Directory Id="APPLICATIONFOLDER" Name="$(var.GlobalProductName)">
<Directory Id="skins" Name="skins"/>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="$(var.GlobalProductName)"/>
</Directory>
</Directory>
<Property Id="ApplicationFolderName" Value="$(var.GlobalProductName)" />
<Property Id="WixAppFolder" Value="WixPerMachineFolder" />
<WixVariable Id="WixUISupportPerUser" Value="0" />
<WixVariable Id="WixUILicenseRtf" Value="gpl-3.0.rtf" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch $(var.GlobalProductName)" />
<Property Id="WixShellExecTarget" Value="[#$(var.Project.TargetFileName)]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<Feature Id="ProductFeature" Title="$(var.ProductName) Core Files" Level="1" Absent="disallow">
<ComponentRef Id="CreateDirectories" />
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="RegistryStuff" />
</Feature>
<UI>
<UIRef Id="WixUI_Advanced" />
<Publish Dialog="ExitDialog" Control="Finish" Event="DoAction" Value="LaunchApplication">WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed</Publish>
</UI>
<!--Hacks for WixUI_Advanced install folder-->
<CustomAction Id="OverwriteWixSetDefaultPerMachineFolder" Property="WixPerMachineFolder" Value="[APPLICATIONFOLDER]" Execute="immediate" />
<CustomAction Id="SetARPINSTALLLOCATION" Property="ARPINSTALLLOCATION" Value="[APPLICATIONFOLDER]" />
<InstallUISequence>
<Custom Action="OverwriteWixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="OverwriteWixSetDefaultPerMachineFolder" After="WixSetDefaultPerMachineFolder" />
<Custom Action="SetARPINSTALLLOCATION" After="InstallValidate"/>
</InstallExecuteSequence>
</Product>
EDIT: I didn't include the code which saves the registry value but it was being saved as '"[APPLICATIONFOLDER]"' and removing the double quotes fixed it for a fresh install.
Also forgot brackets in the SetDirectory

Set WIXUI_INSTALLDIR to value from registry or default if that is blank?

I would like to be able to read a value from the registry and use it to set the WIXUI_INSTALLDIR before the dialog is displayed but if the value from the registry is blank I want to use a default folder.
I'm new to wix.
I've been reading tutorials, documentation, and questions/answers all day and my head is spinning. I think I'm close but I don't have all the pieces quite put together.
The code compiles and links fine but when I run the msi I get an error right before the browse for folder dialog is displayed the error is code 2819 "Control [3] on dialog [2] needs a property linked to it."
In summary what I want to do is:
Read previous install path from registry into a property named "PREVIOUSINSTALLFOLDER".
If "PREVIOUSINSTALLFOLDER" is blank, set WIXUI_INSTALLDIR to "INSTALLFOLDER" as defined in the Directory section.
If "PREVIOUSINSTALLFOLDER" is not blank set WIXUI_INSTALLDIR to "PREVIOUSINSTALLFOLDER".
What am I doing wrong?
Thanks,
Erik
Here's my current code:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="EB907F6C-B193-4A40-BA3C-ADF8C069AF34" Name="LaserVault DMS" Language="1033" Version="10.0.0" Manufacturer="Electronic Storage Corp." UpgradeCode="43291cbc-3f74-44ba-ba14-31181bb654bf">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" Keywords="LaserVault DMS Server" Description="LaserVault DMS Server" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" />
<UIRef Id="WixUI_InstallDir"/>
<Property Id="PREVIOUSINSTALLFOLDER">
<RegistrySearch Id="PreviousInstallDir" Root="HKLM" Key="Software\ESC" Name="LVDMSPath" Type="raw"></RegistrySearch>
</Property>
<CustomAction Id="SetToDefault" Property="WIXUI_INSTALLDIR" Value="[INSTALLFOLDER]" Execute="immediate" />
<CustomAction Id="SetToPrevious" Property="WIXUI_INSTALLDIR" Value="[PREVIOUSINSTALLFOLDER]" Execute="immediate" />
<InstallExecuteSequence>
<Custom Action="SetToDefault" After="AppSearch">PREVIOUSINSTALLDIR=""</Custom>
<Custom Action="SetToPrevious" After="AppSearch"><![CDATA[PREVIOUSINSTALLDIR <> ""]]></Custom>
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="LaserVault" Name="LaserVault">
<Directory Id="INSTALLFOLDER" Name="LVDMS" />
</Directory>
<Directory Id="ProgramMenuFolder" Name="Programs">
<Directory Id="ProgramMenuDir" Name="LaserVault DMS" />
</Directory>
<Directory Id="DesktopFolder" Name="Desktop" />
</Directory>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="LVDMSServerConfig" Guid="D9428A16-ECB1-4373-B876-8CF05E7CE37F">
<File Id="LVDMSServerConfig" Source="C:\Projects\LVDMS10\LVDMSServerConfig\LVDMSServerConfig\bin\Debug\LVDMSServerConfig.exe" KeyPath="yes" />
</Component>
<Component Id="LVDMSCore10" Guid="2E956300-78FC-4AFA-8D5D-A2D07B6CB8AE">
<File Id="LVDMSCore10" Source="C:\Projects\LVDMS10\LVDMSServerConfig\LVDMSServerConfig\bin\Debug\LVDMSCore10.dll" KeyPath="yes" />
</Component>
<Component Id="LVDMSInstallationGuide" Guid="ECA2B30A-54CB-4DE4-A659-B429458BDF3A">
<File Id="LVDMSInstallationGuide" Source="\\192.168.0.211\development\HelpFiles\LaserVault_DMS_10\LaserVault_DMS_Installation_Guide\LaserVault_DMS_Installation_Guide.pdf" KeyPath="yes" />
</Component>
</ComponentGroup>
<ComponentGroup Id="Shortcuts" Directory="ProgramMenuDir">
<Component Id="LVDMSServerConfigShortCut">
<Shortcut Id="LVDMSServerConfigShortCut" Name="LVDMS Server Config" Description="LaserVault DMS Server Configuration" Target="[#LVDMSServerConfig]" WorkingDirectory="INSTALLFOLDER" />
<RemoveFolder Id="ProgramMenuDir" On="uninstall"/>
<RegistryValue Root="HKCU" Key="Software\LaserVault\LVDMS" Name="CurrentVersion" Type="string" Value="[ProductVersion]" KeyPath="yes" />
</Component>
</ComponentGroup>
<ComponentGroup Id="RegistryEntries">
<Component Id="RegistryLVDMSPath" Guid="9AE59D2B-EF16-4CAA-8A27-AA5BE00FAA07" Permanent="yes" Directory="TARGETDIR">
<RegistryKey Root="HKLM" Key="Software\ESC">
<RegistryValue Type="string" Name="LVDMSPath" Value="[INSTALLFOLDER]"/>
</RegistryKey>
</Component>
</ComponentGroup>
<Feature Id="Complete" Title="LaserVault DMS Server" Level="1">
<ComponentGroupRef Id="ProductComponents" />
<ComponentGroupRef Id="Shortcuts"/>
<ComponentGroupRef Id="RegistryEntries"/>
</Feature>
</Product>
For future reference it's enough to only have the following:
<!-- Determine the directory of a previous installation (if one exists) -->
<Property Id="INSTALLFOLDER">
<RegistrySearch Id="GetInstallDir" Type="raw" Root="HKLM" Key="Software\ESC" Name="LVDMSPath" />
</Property>
...
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
Neither of the custom actions are required.
I think you were on the right path. I solved it the following way (basically quite similar as you tried to, just the other way round, i.e. set the default value and overwrite it only if the value was found in the registry) using the following steps:
Set WIXUI_INSTALLDIR to the default value as defined by your directory structure:
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
Do a RegistrySearch for your previous installation folder:
<Property Id="PREVIOUSINSTALLFOLDER">
<RegistrySearch Id="GetPreviousInstallFolder" Root="HKLM" Key="SOFTWARE\ESC" Name="LVDMSPath" Type="raw" />
</Property>
Set INSTALLDIR only if a value was found:
<CustomAction Id="SetINSTALLDIR" Property="INSTALLDIR" Value="[PREVIOUSINSTALLFOLDER]" Execute="firstSequence" />
...
<InstallExecuteSequence>
<Custom Action="SetINSTALLDIR" After="AppSearch">PREVIOUSINSTALLFOLDER</Custom>
</InstallExecuteSequence>
<InstallUISequence>
<Custom Action="SetINSTALLDIR" After="AppSearch">PREVIOUSINSTALLFOLDER</Custom>
</InstallUISequence>

msi installation fails, if env path is altered or deleted

I have this wxs file, expected of which is: 1) If env ZENWORKS_HOME present it installs there, 2) If not, it installs to Program Files.
The problem i am facing is:
a) If the ZENWORKS_HOME env path i alter, default is C:\Program Files\Novell\ZENworks\bin\preboot which i alter to D:\, the msi installation fails with error code
b) If i delete the ZENWORKS_HOME env variable, then also it fails to install.
Below is my code snippet of wxs file:
<Wix xmlns="http://schemas.microsoft.com/wix/2003/01/wi">
<!-- TODO: Create new GUIDs for the Product and Package Id. -->
<!-- TODO: Create a GUID for the UpgradeCode. This GUID must remain the same forever more
for your MSI...at least as long as you want the MSI engine to actually upgrade
between versions of your MSI -->
<!-- TODO: Fill out the Description field and the Product Name. -->
<Product UpgradeCode="5CB46018-7411-4049-BB2D-CF7414BC8618" Id="????????-????-????-????-????????????" Name="ZENworks Image Explorer" Language="1033" Version="$(var.version)" Manufacturer="Novell, Inc.">
<Package Id="????????-????-????-????-????????????" Description="ZENworks Image Explorer allows to you to view and modify the contents of a ZENworks image." InstallerVersion="200" Compressed="yes" />
<Condition Message="ZENworks Image Explorer requires that your computer is running Windows XP or Windows 2003 Server"><![CDATA[(Not Version9X) And (VersionNT > 500)]]></Condition>
<Media Id="1" Cabinet="Product.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLATIONDIRECTORY" Name="DefltDir">
<Directory Id="bin" Name="bin">
<Directory Id="preboot" Name="preboot">
<Component Id="ZMGEXP_Start_script" Guid="CC1958FD-E438-4C13-9C97-D0A629AFB6A4" DiskId="1">
<File Id="zmgexp.bat" LongName="zmgexp.bat" Name="ZMGEXP1.BAT" Source="zmgexp.bat" />
</Component>
<Directory Id="nls" Name="nls">
<Component Id="ZMGEXP_Help_files" Guid="F70E341C-2B27-4FED-B1DF-DC8C311FF322" DiskId="1">
<File Id="ImgExplorerHelp.html" Name="IMGEXPH1.htm" Source="resources\helpfiles\ImgExplorerHelp.html" LongName="ImgExplorerHelp.html" />
</Component>
</Directory>
</Directory>
</Directory>
<Directory Id="lib" Name="lib">
<Directory Id="java" Name="java">
<Component Id="ZMGEXP_jar" Guid="B0F2E4AA-17AC-4B0A-8560-9DA8CE17790F" DiskId="1">
<File Id="zmgexp.jar" LongName="zmgexp.jar" Name="ZMGEXP1.JAR" Source="zmgexp.jar" />
</Component>
<Component Id="SWT_64_jar" Guid="51A737C4-F199-4366-A3A0-D374A7800A96" DiskId="1" Win64="yes">
<Condition>VersionNT64</Condition>
<File Id="swt_64.jar" LongName="swt.jar" Name="SWT_64.JAR" Source="swt-win64\swt.jar" />
</Component>
<Component Id="SWT_32_jar" Guid="9B01F63C-F125-4f6d-B729-A2CE6B303E35" DiskId="1" Win64="no">
<Condition>NOT VersionNT64</Condition>
<File Id="swt_32.jar" LongName="swt.jar" Name="SWT_32.JAR" Source="swt-win32\swt.jar" />
</Component>
.
.
.<snip>
<Upgrade Id="5CB46018-7411-4049-BB2D-CF7414BC8618">
<UpgradeVersion IgnoreRemoveFailure="yes" IncludeMaximum="yes" Maximum="$(var.version)" Property="UPGRADE_VERSIONS"/>
<UpgradeVersion OnlyDetect="yes" IgnoreRemoveFailure="yes" IncludeMinimum="no" Minimum="$(var.version)" Property="NEWER_VERSIONS"/>
</Upgrade>
<Property Id="UPGRADE_VERSIONS" Secure="yes"/>
<Property Id="NEWER_VERSIONS" Secure="yes"/>
<CustomAction Id="SetInstallationDirectory"
Directory="INSTALLATIONDIRECTORY"
Value="[%ZENWORKS_HOME]" />
<CustomAction Id="NEWER_VERSION_PRESENT" Error="A newer version already exists"/>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallFinalize"/>
<Custom Action="NEWER_VERSION_PRESENT" After="FindRelatedProducts">
NEWER_VERSIONS
</Custom>
<Custom Action="SetInstallationDirectory" After='CostFinalize'>[%ZENWORKS_HOME%]</Custom>
</InstallExecuteSequence>
</Product>
</Wix>

How upgrade "secondary" executable using Wix?

I have msi setup written in Wix 3. Nothing too fancy, couple executables + some registry manipulations.
We have 2 executables, the main application and watch dog application that is always running(it's started once user logs in).
I've released new version of our software, in wix we specify to upgrade without uninstalling first. When new version msi is ran, it upgrades main application but watchdog application is not upgraded(even if it's not running, so it's not "in-use" problem)
I pasted below my wxs file, hopefully you can help me spot the problem.
Thanks a lot in advance,
Jack
This is the wxs file:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include Properties.wxi?>
<Product Id="$(var.ProductCode)" Name="$(var.Title)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="$(var.Manufacturer)" UpgradeCode="$(var.UpgradeCode)">
<Package Description="$(var.ProductName)" Comments="$(var.ProductName)" InstallerVersion="200" Compressed="yes" />
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<Upgrade Id="$(var.UpgradeCode)">
<UpgradeVersion OnlyDetect='yes' Property='NEWERFOUND' Minimum='$(var.ProductVersion)' IncludeMinimum='no' />
<UpgradeVersion OnlyDetect="no" Property="PREVIOUSFOUND" Minimum="0.0.0" IncludeMinimum="yes" Maximum="$(var.ProductVersion)" IncludeMaximum="no" />
</Upgrade>
<Media Id="1" Cabinet="product.cab" EmbedCab="yes" CompressionLevel="high"/>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder" Name="PFiles">
<Directory Id="ApplicationRootFolder" Name="$(var.ProductName)">
<Component Id="MyAppComponent" Guid="6657828D-0D3F-43A6-A2CB-375BFBB8A412">
<RemoveFolder Id="ApplicationRootFolder" On="uninstall" />
<File Id="MyAppMain" Name="MyApp.exe" Source="..\Release\MyApp.exe"/>
<File Id="FirstRunApp" Name="FirstRunApp.exe" Source="..\Release\FirstRunApp.exe"/>
<File Id="MyAppWatchDog" Name="MyAppWatchDog.exe" Source="..\Release\MyAppWatchDog.exe"/>
<!--File Id="CheckSniffer" Name="CheckSniffer.exe" Source="..\Release\CheckSniffer.exe"/-->
<File Id="License" Name="License.rtf" Source="License.rtf"/>
</Component>
</Directory>
</Directory>
<Directory Id="DesktopFolder" >
<Component Id="DesktopShortcuts" Guid="5567ED7E-03F8-4AB8-AFB5-F8074F4ED1B5">
<RegistryValue Id="RegShortcutDesktop" Root="HKCU" Key="Software\$(var.ProductName)" Name="DesktopSC" Value="1" Type="integer" KeyPath="yes" />
<Shortcut Id="DesktopShortcut" Name="$(var.ProductName)" Directory="DesktopFolder" Description="$(var.ProductName)" Target="[ApplicationRootFolder]$(var.Name).exe" Icon="Icon.exe" WorkingDirectory="ApplicationRootFolder" />
</Component>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ProjectMenuFolder" Name="$(var.ProductName)">
<Component Id="StartMenuShortcuts" Guid="5567ED7E-03F8-4AB8-AFB5-F8074F4ED1B4">
<RegistryValue Root="HKCU" Key="Software\$(var.ProductName)" Type="string" Value="[ApplicationRootFolder]" Name="InstallPath"/>
<RegistryValue Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\Run" Type="string" Value="[#MyAppWatchDog]" Name="MyAppWatchdog"/>
<RegistryValue Root="HKCU" Key="Software\$(var.ProductName)" Type="string" Value="" />
<Shortcut Id="ProgramMenuShortcut" Name="$(var.ProductName)" Description="$(var.ProductName)" Target="[ApplicationRootFolder]$(var.Name).exe" Icon="Icon.exe" WorkingDirectory="ApplicationRootFolder" />
<Shortcut Id="UninstallProduct" Name="Uninstall" Description="Uninstalls the $(var.ProductName)" Target="[System64Folder]msiexec.exe" Arguments="/x {$(var.ProductCode)}" />
<RemoveFolder Id="ProgramMenuDir" On="uninstall" />
<RemoveRegistryKey Root="HKCU" Key="Software\$(var.ProductName)" Action="removeOnUninstall"/>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="DefaultFeature" Title="$(var.Name)" Level="1">
<ComponentRef Id="MyAppComponent" />
<ComponentRef Id="StartMenuShortcuts" />
<ComponentRef Id="DesktopShortcuts" />
</Feature>
<Property Id="SEARCHWINPCAP">
<RegistrySearch Id="RegistrySearch" Root="HKLM" Key="SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\WinPcapInst" Name="VersionMajor" Type="raw"/>
</Property>
<Property Id="WIXUI_INSTALLDIR" Value="ApplicationRootFolder"/>
<Property Id="PRODUCTNAME" Value="$(var.ProductName)"/>
<!--Property Id="SNIFFER_CHECK_RESULT" Value="0"/-->
<WixVariable Id="WixUIBannerBmp" Value="logo.JPG" />
<WixVariable Id="WixUILicenseRtf" Value="License.rtf" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOXTEXT" Value="Launch $(var.Title)" />
<Property Id="WIXUI_EXITDIALOGOPTIONALCHECKBOX" Value="1" />
<Property Id="WixShellExecTarget" Value="[#MyAppMain]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<CustomAction Id="SetLaunchWatchDogTarget" Property="WixShellExecTarget" Value="[#MyAppWatchDog]" />
<CustomAction Id="LaunchWatchDog" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />
<Binary Id="BinBrowseForFile" SourceFile="tools\WinPcap_4_1_2.exe"/>
<CustomAction Id='LaunchWinPcapSetup' BinaryKey="BinBrowseForFile" ExeCommand="/Install" Execute="deferred" Impersonate="no" Return="check"/>
<CustomAction Id='NoDowngrade' Error='A later version of [ProductName] is already installed.'/>
<Property Id="CUSTOM_TITLE" Value="$(var.Title) Setup" />
<CustomAction Id='SetRepearTitle' Property='CUSTOM_TITLE' Value='Repair $(var.Title)'/>
<CustomAction Id='SetRemoveTitle' Property='CUSTOM_TITLE' Value='Remove $(var.Title)'/>
<!--debug-->
<Binary Id="BinCustomAction" SourceFile="bin\release\CustomActions.CA.dll"/>
<!--Binary Id="BinCheckSniffer" SourceFile="..\Release\CheckSniffer.exe"/-->
<Property Id="USER_GUID_PATH" Value="Software\Licenses"/>
<Property Id="USER_GUID" />
<Property Id="DUBUG_URL" Value="http://debug.myapp.com/service/"/>
<Property Id="DUBUG_STATE" Value="STARTED" />
<CustomAction Id='SendRequest' BinaryKey="BinCustomAction" DllEntry="SendRequest" Execute="immediate" Return="check" />
<CustomAction Id='SendRequest_WinPcap_Start' BinaryKey="BinCustomAction" DllEntry="SendRequest" Execute="immediate" Return="check" />
<CustomAction Id='SendRequest_WinPcap_Finish' BinaryKey="BinCustomAction" DllEntry="SendRequest" Execute="immediate" Return="check" />
<CustomAction Id='LicenseAgreement_Next' Property='DUBUG_STATE' Value='LICENSE_AGREED' />
<CustomAction Id='InstallDirDlg_Install' Property='DUBUG_STATE' Value='INSTALL' />
<CustomAction Id='Exit_Finish' Property='DUBUG_STATE' Value='FINISH' />
<CustomAction Id='WinPcap_Start' Property='DUBUG_STATE' Value='WINPCAP_STARTED' />
<CustomAction Id='WinPcap_Finish' Property='DUBUG_STATE' Value='WINPCAP_FINISHED' />
<CustomAction Id='Traffic_Failed' Property='DUBUG_STATE' Value='TRAFFIC_FAILED' />
<CustomAction Id='Traffic_Uninstalled' Property='DUBUG_STATE' Value='UNINSTALLED' />
<CustomAction Id='Shell_icacls' Directory='ApplicationRootFolder' ExeCommand='icacls FirstRunApp.exe /setintegritylevel Low' Return="ignore" Execute="commit" Impersonate="no"/>
<InstallExecuteSequence>
<Custom Action='NoDowngrade' After='FindRelatedProducts'>NEWERFOUND</Custom>
<Custom Action="WinPcap_Start" Before="SendRequest_WinPcap_Start" ><![CDATA[SEARCHWINPCAP < "4" AND NOT Installed]]></Custom>
<Custom Action="SendRequest_WinPcap_Start" Before="LaunchWinPcapSetup"><![CDATA[SEARCHWINPCAP < "4" AND NOT Installed]]></Custom>
<Custom Action="LaunchWinPcapSetup" Before="InstallFiles"><![CDATA[SEARCHWINPCAP < "4" AND NOT Installed]]></Custom>
<!-- disable currently Custom Action="CheckSniffer" Before="Shell_icacls">NOT Installed</Custom-->
<Custom Action="Shell_icacls" Before="InstallFinalize"><![CDATA[VersionNT >= 600 AND NOT Installed]]></Custom>
<!--Custom Action="Shell_RunWatchDog" After="InstallFinalize"><![CDATA[NOT Installed]]></Custom-->
<RemoveExistingProducts After="InstallFinalize"></RemoveExistingProducts>
<Custom Action="Traffic_Uninstalled" Before='SendRequest'>Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
<Custom Action="SendRequest" After='InstallFinalize'>Installed AND NOT UPGRADINGPRODUCTCODE</Custom>
</InstallExecuteSequence>
<UI />
<UIRef Id="WixUI_Wizard" />
<PropertyRef Id="NETFRAMEWORK35" />
<Condition Message="This application requires .NET Framework 3.5 SP1.">Installed OR NETFRAMEWORK35</Condition>
<Icon Id="Icon.exe" SourceFile="..\MyApp\Resources\icon_main.ico" />
</Product>
</Wix>
The first thing that jumps at me from your Wix code is the fact that you are shipping all executables inside a single component but there is no KeyPath for the component. This is a very bad idea! It will definitely result in many servicing issues, such as the one you're experiencing with your watchdog.exe not updating.
Windows Installer considers a component the smallest installation unit. Furthermore, the Windows Installer will only look at the KeyPath to decide whether the component exists (or needs to be updated, etc.) Since the "MyAppComponent" has no keypath, the WI will default to using the Directory as your keypath. This means that during an upgrade, if WI finds your directory, it will assume the component is already installed and will not update any of the files in the component.
The rule of thumb to follow is that any .exe or .dll file must be the only file in the component, and it should also be the keypath for your component. I go as far as creating a component for each individual file in my setups, which will avoid many headaches in the future. Try replacing the MyAppComponent with these components instead:
<Directory Id="ApplicationRootFolder" Name="$(var.ProductName)">
<Component Id="MyApp.exe" Guid="INSERT_GUID_HERE">
<File Id="MyApp.exe" Name="MyApp.exe" Source="..\Release\MyApp.exe" KeyPath="yes"/>
</Component>
<Component Id="FirstRunApp.exe" Guid="INSERT_GUID_HERE">
<File Id="FirstRunApp.exe" Name="FirstRunApp.exe" Source="..\Release\FirstRunApp.exe" KeyPath="yes"/>
</Component>
<Component Id="MyAppWatchDog.exe" Guid="INSERT_GUID_HERE">
<File Id="MyAppWatchDog.exe" Name="MyAppWatchDog.exe" Source="..\Release\MyAppWatchDog.exe" KeyPath="yes"/>
</Component>
<Component Id="CheckSniffer.exe" Guid="INSERT_GUID_HERE">
<File Id="CheckSniffer.exe" Name="CheckSniffer.exe" Source="..\Release\CheckSniffer.exe" KeyPath="yes"/>
</Component>
<Component Id="License.rtf" Guid="INSERT_GUID_HERE">
<File Id="License.rtf" Name="License.rtf" Source="License.rtf" KeyPath="yes"/>
</Component>
</Directory>
This should resolve your updating issues. You may also want to conisder splitting your shortcut components into one shortcut per component, and setting an HKCU value as the keypath for each component.
Thanks to everyone trying to help.
#Cosmin Pirvu was the one with correct answer. We didn't update the version of watchdog application and thus it was not being installed.
Thank you