I'm having trouble accomplishing a task with the Wix Toolset. In particular, I have a scenario where I have an MSI that configures an MSM (Configurable Module). The MSI has a custom UI dialogue from which user input is to be used to configure the MSM.
When I try to configure the MSM using a hardcoded value for the address property as shown below it works fine and the MSM is configured correctly. (I believe this configuration happens and build-time as opposed to run-time - the issue may lay there.).
The problem occurs when I use a custom dialogue to set the value of the address property at installation time (i.e run-time). The configurable module still uses the hardcoded value, rather than the users input. Is the problem because merge module configuration is done during build-time only. Is there a way to pass a value to the merge module from the UI of the main MSI?
Here is an over simplified version:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="cf1d176d-2d57-435e-8e7f-abba14de821c" Language="1033">
<Media Id="1" Cabinet="SemanticEvolution.cab" EmbedCab="yes" />
<Property Id="Address" Value="http://127.0.0.1" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Semantic Evolution">
<Merge Id="MergeModule" Language="1033" SourceFile="Module.msm" DiskId="1">
<ConfigurationData Name="EndpointAddressConfiguration" Value="[Address]" />
</Merge>
</Directory>
</Directory>
</Directory>
<Feature Id="SemanticEvolutionFeatures" Title="Semnatic Evolution" Level="1">
<Feature Id="TestFeature" Title="TestFeature" Level="1">
<MergeRef Id="MergeModule" />
</Feature>
</Feature>
<UI Id="CustomWixUI">
<UIRef Id="WixUI_FeatureTree" />
<DialogRef Id="ConfigurationDlg" />
<Publish Dialog="LicenseAgreementDlg" Control="Next" Event="NewDialog" Value="ConfigurationDlg">LicenseAccepted = "1"</Publish>
<Publish Dialog="CustomizeDlg" Control="Back" Event="NewDialog" Value="ConfigurationDlg">NOT Installed</Publish>
</UI>
</Product>
</Wix>
Here is a snipet of the merge module:
<Configuration Name="EndpointAddressConfiguration" Format="Text" />
<Substitution Table="CustomAction" Row="SetEndpointAddress" Column="Target" Value="[=EndpointAddressConfiguration]" />
<CustomAction Id="SetEndpointAddress" Property="EndpointAddress" Value="[EndpointAddress]" />
<InstallExecuteSequence>
<Custom Action="SetEndpointAddress" Before="LaunchConditions">1</Custom>
</InstallExecuteSequence>
Eventually in the merge module the configured property is used as follows:
<util:XmlFile Id="EndpointAddress" Action="setValue" ElementPath="/configuration/system.serviceModel/client/endpoint/#address" File="[#Se.Gui.exe.config]" Value="[EndpointAddress]/ApiDataService"/>
Remember that public properties must be in UPPERCASE.
You can find the answer here:
http://windows-installer-xml-wix-toolset.687559.n2.nabble.com/Passing-properties-to-merge-modules-td5417112.html
To access a property from a merge module you must append the merge module id to the property name. Something like: MyProp.msm_guid
http://msdn.microsoft.com/en-us/library/aa370051(VS.85).aspx
Related
I am creating a new WIX installer. The installation is based on the customer's existing database. At the very first installation I have the information of the already installed features only in the database. So I have to query theese features from it and the installer shouldn't execute its activation scripts. Every feature has a property and from a custom action I set theese properties but the conditional SQL script execution is not working. I've created a sample wxs source file. Could you help me what is wrong with it?
<?xml version='1.0' encoding='UTF-8'?>
<Wix xmlns='http://schemas.microsoft.com/wix/2006/wi' xmlns:util='http://schemas.microsoft.com/wix/UtilExtension' xmlns:sql='http://schemas.microsoft.com/wix/SqlExtension'>
<!-- https://wixtoolset.org/documentation/manual/v3/xsd/wix/product.html -->
<Product Id='BC075295-7BB8-4B82-89AC-3F81681130CC' Name='XXX' UpgradeCode='4AD0BCB8-B1BB-4FE1-ABEE-58E93321AAC5' Language='1033' Codepage='1252' Version='1.0.0' Manufacturer='XXX'>
<Package Id='*' Keywords='Installer' Description="XXX Installer" InstallerVersion='100' Languages='1033' Compressed='yes' SummaryCodepage='1252'/>
<Media Id='1' Cabinet='Andoc.cab' EmbedCab='yes' />
<Binary Id="WixCustomActions" SourceFile="CustomAction.CA.dll" />
<Binary Id="sqlScriptBinaryKey" SourceFile="insert.sql" />
<CustomAction Id="SetExecuteScriptCondition" BinaryKey="WixCustomActions" DllEntry="SetExecuteScriptCondition" Execute="immediate" Return="check" />
<Property Id="EXECUTE_SCRIPT" Value="NO" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="XXX" />
</Directory>
<Component Id="componentSqlScript" Directory="INSTALLDIR" Guid="1af938ef-6788-a0e1-2370-a6c2620c9dCC">
<Condition>EXECUTE_SCRIPT = "YES"</Condition>
<sql:SqlDatabase Id="db" Database="[DATABASE_NAME]" Server="[DATABASE_SERVER]" CreateOnInstall="yes" DropOnUninstall="no" ContinueOnError="no">
<sql:SqlScript Id="SqlScriptId" BinaryKey="sqlScriptBinaryKey" ExecuteOnInstall="yes" ExecuteOnUninstall="no" ContinueOnError="no" />
</sql:SqlDatabase>
<CreateFolder />
</Component>
<Feature Id="feature" Title="xxx" Description="xxx" ConfigurableDirectory="INSTALLDIR" Level="1">
<ComponentRef Id="componentSqlScript" />
</Feature>
<InstallExecuteSequence>
<Custom Action='SetExecuteScriptCondition' Before='InstallInitialize' />
</InstallExecuteSequence>
<UI>
<UIRef Id="WixUI_Mondo" />
</UI>
</Product>
</Wix>
My custom action looks like this.
[CustomAction]
public static ActionResult SetExecuteScriptCondition(Session session)
{
session["EXECUTE_SCRIPT"] = "YES";
return ActionResult.Success;
}
In the log I see that the EXECUTE_SCRIPT property value is 'YES' but the insert.sql script is not executed.
I've found a workaround to solve this problem by creating a subfeature to the sql component and moving the component condition to the feature level. And instead of InstallExecuteSequence I execute my custom action in InstallUISequence.
<Feature Id="feature" Title="xxx" Description="xxx" ConfigurableDirectory="INSTALLDIR" Level="1">
<Feature Id="feature_script" Title="script" Description="script" Display="hidden" Level="1" >
<Condition Level="0">EXECUTE_SCRIPT = "NO"</Condition>
<Condition Level="1">EXECUTE_SCRIPT = "YES"</Condition>
<ComponentRef Id="componentSqlScript" />
</Feature>
</Feature>
<InstallUISequence>
<Custom Action='SetExecuteScriptCondition' Before='LaunchConditions' />
</InstallUISequence>
InstallInitialize is too late, as the condition for your component 'componentSqlScript' is evaluated during costing. That's the reason setting that property didn't appear to work.
You need to schedule your action before 'CostInitialize', sequence it in both UI and Execute sequences (since the UI sequence doesn't necessarily run, and if costing is also preformed in the UI sequence then the execute sequence will be too late). You should add Execute="firstSequence" to your CustomAction element, and you should add Secure="yes" to your Property element, to prevent running code excessively and prevent issues where public property values aren't always set to the execute sequence.
I am trying to create a dual purpose MSI file using WiX. I have followed the instructions for WixUI_Advanced as well as the instructions for Single Package Authoring. This seems to work fine when I default to having a per user install by default (MSIINSTALLPERUSER = 1) and allow the user to select a per machine install. However setting it to install per machine by default (MSIINSTALLPERUSER empty) always results in a UAC prompt even when the user selects a per user install. The per user install is only writing a single file to a non admin directory and definitely does not require elevated privileges.
I have also tried following this guide which everyone seems to be using to do Single Package Authoring with WiX but the results are exactly the same. A UAC prompt appears if per machine is the default and per user is selected but not if per user is the default and per user is selected.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="DE75C3B3-6398-4F25-9048-FB7EEE5F6EBF" Name="MyApp" Language="1033" Version="1.0.0" Manufacturer="Company" UpgradeCode="ED573078-CC3E-4299-9E04-043B1EDC08DB">
<Package InstallerVersion="500" Compressed="yes" />
<!--Single Package Authoring-->
<Property Id="MSIINSTALLPERUSER" Secure="yes" Value="{}"/>
<Property Id="ALLUSERS" Secure="yes" Value="2"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate EmbedCab="yes" CabinetTemplate="arc{0}" CompressionLevel="high"/>
<Feature Id="ProductFeature" Title="MyApp" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
<UI>
<UIRef Id="WixUI_Advanced" />
</UI>
<Property Id="ApplicationFolderName" Value="MyApp" />
<Property Id="WixAppFolder" Value="WixPerMachineFolder" />
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder" Name="PFiles">
<Directory Id="APPLICATIONFOLDER" Name="MyApp">
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="APPLICATIONFOLDER">
<Component Id="MyApp.exe" Guid="903EDAFD-F513-407D-85A0-D737013B9B57">
<File Id="MyApp.exe" Source="MyApp.exe" KeyPath="yes" Checksum="yes"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
Looking through the install log I see the following entries:
Product not registered: beginning first-time install
PROPERTY CHANGE: Modifying ALLUSERS property. Its current value is '2'. Its new value: '1'.
PROPERTY CHANGE: Deleting MSIINSTALLPERUSER property. Its current value is '{}'.
...
Action: InstallScopeDlg. Dialog created
PROPERTY CHANGE: Modifying WixAppFolder property. Its current value is 'WixPerMachineFolder'. Its new value: 'WixPerUserFolder'.
PROPERTY CHANGE: Deleting ALLUSERS property. Its current value is '1'.
...
Product not registered: beginning first-time install
PROPERTY CHANGE: Deleting ALLUSERS property. Its current value is '2'.
PROPERTY CHANGE: Deleting MSIINSTALLPERUSER property. Its current value is '{}'.
Based on the install log I tried reintroducing the lines replaced in the modified WixUI file from this guide which update the ALLUSERS property as it seems as though this was being set to a value of 1 for the per user install which would explain the UAC prompt. Having both the following lines from the Russian blog and the original WixUI_Advanced does seem to work.
<Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="{}" Order="2">
WixAppFolder = "WixPerUserFolder"
</Publish>
<Publish Dialog="InstallScopeDlg" Control="Next" Property="ALLUSERS" Value="1" Order="3">
WixAppFolder = "WixPerMachineFolder"
</Publish>
<Publish Dialog="InstallScopeDlg" Control="Next" Property="MSIINSTALLPERUSER" Value="1" Order="3">
WixAppFolder = "WixPerUserFolder"
</Publish>
<Publish Dialog="InstallScopeDlg" Control="Next" Property="MSIINSTALLPERUSER" Value="{}" Order="2">
WixAppFolder = "WixPerMachineFolder"
</Publish>
It seems as though both ALLUSERS and MSIINSTALLPERUSER needs to be set based on the user's choice to allow installation with no Administrator privileges when a per machine install is the default. I can't find anywhere else online to confirm my findings however.
There are three properties to control the default install scope when using WixUI_Advanced UI: 'ALLUSERS', 'Privileged' and 'MSIINSTALLPERUSER'.
The property 'WixAppFolder' will control which radio button is selected by default when 'advanced' button is clicked.
And there is a konwn bug of wix needs a workaround: https://github.com/wixtoolset/issues/issues/2376
In summary, the wix config can be:
Per user
<Property Id='WixAppFolder' Value='WixPerUserFolder' />
<Property Id='ALLUSERS' Value='2' />
<Property Id='Privileged' Value='0' />
<Property Id='MSIINSTALLPERUSER' Value='1' />
<UI>
<UIRef Id='WixUI_Advanced' />
<UIRef Id='WixUI_ErrorProgressText' />
<Publish Dialog='InstallScopeDlg' Control='Next' Property='MSIINSTALLPERUSER' Value='1' Order='3'>WixAppFolder = "WixPerUserFolder"
<Publish Dialog='InstallScopeDlg' Control='Next' Property='MSIINSTALLPERUSER' Value='{}' Order='2'>WixAppFolder = "WixPerMachineFolder"
<Publish Dialog='InstallScopeDlg' Control='Next' Event='DoAction' Value='WixSetDefaultPerMachineFolder' Order='3'>WixAppFolder = "WixPerMachineFolder"
<Publish Dialog='InstallScopeDlg' Control='Next' Event='DoAction' Value='WixSetDefaultPerUserFolder' Order='3'>WixAppFolder = "WixPerUserFolder"
</UI>
Per machine
<Property Id='WixAppFolder' Value='WixPerMachineFolder' />
<Property Id='ALLUSERS' Value='1' />
<Property Id='Privileged' Value='1' />
<Property Id='MSIINSTALLPERUSER' Value='0' />
<UI>
<UIRef Id='WixUI_Advanced' />
<UIRef Id='WixUI_ErrorProgressText' />
<Publish Dialog='InstallScopeDlg' Control='Next' Property='MSIINSTALLPERUSER' Value='1' Order='3'>WixAppFolder = "WixPerUserFolder"
<Publish Dialog='InstallScopeDlg' Control='Next' Property='MSIINSTALLPERUSER' Value='{}' Order='2'>WixAppFolder = "WixPerMachineFolder"
<Publish Dialog='InstallScopeDlg' Control='Next' Event='DoAction' Value='WixSetDefaultPerMachineFolder' Order='3'>WixAppFolder = "WixPerMachineFolder"
<Publish Dialog='InstallScopeDlg' Control='Next' Event='DoAction' Value='WixSetDefaultPerUserFolder' Order='3'>WixAppFolder = "WixPerUserFolder"
</UI>
BTW, I created a project to simplify the configuration of wix. Hope it can help: https://github.com/xinnj/WiXCover
I have used Wix on and off for almost a year. After a little break and now I am back to wix and need to build a wix Msi again but I found a very strange thing that I haven't met before. After I created the msi file and and copy the msi to somewhere to install. During the installation, it shows an error that it can't find the source files from the folder: current msi location\EasyLobby\Cogito. I was wondering why it tried to find the source files from that location. I then found that from the project during the compilation, it always creates \EasyLobby\Cogito under the bin\Debug folder. So if I run the msi right from the ...bin\Debug, it runs OK because the \EasyLobby\Cogito folder is there. \
It seems so strange. The msi file should include all the source files and shouldn't look for source files somewhere else. Here is the product.wxs file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupCogito" Language="1033" Version="2.0.0.0" Manufacturer="Microsoft" UpgradeCode="96cb03c9-6a03-4344-b816-20a0bb9e5df0">
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<Media Id="1" Cabinet="Server.cab" EmbedCab="yes" />
<!--<MediaTemplate />-->
<Feature Id="ProductFeature" Title="SetupCogito" Level="1">
<ComponentGroupRef Id="ComponentGroup"/>
<ComponentRef Id="EasyLobbyCogitoShortcut" />
</Feature>
<WixVariable Id="WixUILicenseRtf" Value="Files\LicenseAgmt.rtf"/>
<UIRef Id="CogitoUI_Installdir" />
<Property Id="WIXUI_INSTALLDIR" Value="COGITOFOLDER" />
<Binary Id="banner_bmp" SourceFile="Files\Banner.bmp"/>
<Property Id ="PIDTemplate" >
<![CDATA[&&&-&&&&&&-&&&&-&&&&]]>
</Property>
<Icon Id="ELCogitoConfig.exe" SourceFile="..\CogitoIntegration\ELCogitoConfig.exe" />
</Product>
<Fragment>
<Binary Id="CustomActions"
SourceFile="..\CustomActions\bin\Debug\CustomActions.CA.dll" />
<CustomAction Id="IsValidKeyCode"
BinaryKey="CustomActions"
DllEntry="IsValidKeyCode"
Execute="immediate"
Return="check" />
<InstallExecuteSequence>
<Custom Action="IsValidKeyCode"
Before='InstallFinalize'>NOT Installed</Custom>
</InstallExecuteSequence>
</Fragment>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLFOLDER" Name="EasyLobby" >
<Directory Id="COGITOFOLDER" Name="Cogito"/>
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="ApplicationProgramsFolder" Name="EasyLobby Cogito" />
</Directory>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="COGITOFOLDER">
<Component Id="EasyLobbyCogitoShortcut" Guid="{BFE6EB30-0F71-4F92-8D93-84B4EBF41F0E}" >
<File Id="Easy" Source="$(var.SourceDir)\ELCogitoConfig.exe" />
<Shortcut Id="ApplicationStartMenuShortcut" Name="Cogito Configuration" Directory="ApplicationProgramsFolder"
WorkingDirectory='INSTALLDIR' Icon="ELCogitoConfig.exe" IconIndex="0" Advertise="yes"/>
</Component>
</DirectoryRef>
And this is the config for heat in project file:
<Target Name="BeforeBuild">
<PropertyGroup>
<DefineConstants>SourceDir= C:\Development\SetupCogito\CogitoIntegration;</DefineConstants>
<LinkerBaseInputPaths>..\CogitoIntegration\</LinkerBaseInputPaths>
</PropertyGroup>
<HeatDirectory OutputFile="CogitoSetup.wxs" Directory="..\CogitoIntegration" PreprocessorVariable="var.SourceDir" DirectoryRefId="COGITOFOLDER" ComponentGroupName="ComponentGroup" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" AutoGenerateGuids="false" GenerateGuidsNow="true" ToolPath="$(WixToolPath)" />
This line here is your problem:
<Package InstallerVersion="200" Compressed="no" InstallScope="perMachine" />
Change Compressed=no to Compressed=yes and it will include all the source files in the finished MSI. If you don't compress it, the finished files are not included in the MSI and you get an error if it can't find them at runtime.
See Package reference.
I'm having a problem with the Wix installer and don't know what specifically I'm missing.
I have enabled the dialog to allow user pick their own installation folder over the default of
If I run the install and just leave the default of "Program Files\MyApp", the app installs no problem. If I then uninstall, all the files and folders properly get deleted.
If I run the install and change the default to something like c:\TestFolder\MyApp, it installs no problem to that location and includes the subfolder for the Java64bit and Java64bit/lib folders with respective content.
Then, if I immediately do an uninstall, it properly removes the Java stuff completely, but leaves all the files within the c:\TestFolder\MyApp root.
Looking into the registry after install, it DOES show the proper install directory location. What else can I provide to help resolve this.
Here is the full (with GUIDs removed) install of different files
File for the main (sample) product
<?xml version="1.0" encoding="utf-32BE"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product
Name="AMyApp"
Manufacturer="Testing"
Language="1033"
Version="1.0.1.0"
Id="*"
UpgradeCode="{GUID1}" >
<?include AMyApp_Define_Paths.wxi ?>
<Package
Id="*"
InstallerVersion="200"
Platform="x64"
Compressed="yes"
Languages="1033"
SummaryCodepage="1252"
Comments="AMyApp Package Comments area"
Description ="AMyApp"
InstallScope="perMachine" />
<MajorUpgrade
AllowDowngrades="no"
AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A newer version of AMyApp is already installed." />
<Property Id="PREVIOUSVERSIONSINSTALLED" Secure="yes" />
<Upgrade Id="{GUID1}">
<UpgradeVersion
Minimum="4.0.0.0" Maximum="99.0.0.0"
Property="PREVIOUSVERSIONSINSTALLED"
IncludeMinimum="yes" IncludeMaximum="no" />
</Upgrade>
<Media Id="1"
Cabinet="AMyApp.cab"
EmbedCab="yes" />
<WixVariable Id="ALLUSERS" Value="2" />
<WixVariable Id="MSIINSTALLPERUSER" Value="" />
<!-- see if location from a previously installed instance. -->
<Property Id="INSTALLDIR" >
<RegistrySearch Id="AMYAPP" Type="raw"
Root="HKLM" Key="SOFTWARE\AMYAPP" Name="InstallDir" />
</Property>
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLDIR" />
<!-- Search registry for previous installed location -->
<Property Id="PREVIOUSINSTALLFOLDER">
<RegistrySearch Id="GetPreviousInstallFolder" Root="HKLM"
Key="SOFTWARE\AMYAPP" Name="INSTALLDIR" Type="raw" />
</Property>
<CustomAction Id="SetINSTALLDIR" Property="INSTALLDIR"
Value="[PREVIOUSINSTALLFOLDER]" Execute="firstSequence" />
<CustomAction Id="SetWIXUI_INSTALLDIR" Property="WIXUI_INSTALLDIR"
Value="[PREVIOUSINSTALLFOLDER]" Execute="firstSequence" />
<InstallExecuteSequence>
<Custom Action="SetINSTALLDIR"
After="AppSearch">PREVIOUSINSTALLFOLDER</Custom>
<Custom Action="SetWIXUI_INSTALLDIR"
After="AppSearch">PREVIOUSINSTALLFOLDER</Custom>
</InstallExecuteSequence>
<InstallUISequence>
<Custom Action="SetINSTALLDIR"
After="AppSearch">PREVIOUSINSTALLFOLDER</Custom>
<Custom Action="SetWIXUI_INSTALLDIR"
After="AppSearch">PREVIOUSINSTALLFOLDER</Custom>
</InstallUISequence>
<Directory Id="TARGETDIR" Name="SourceDir" >
<!-- The directories "DesktopFolder", "FontsFolder", "ProgramFilesFolder"
are all pre-defined common names via the installer and reference the
actual windows locations respectively -->
<Directory Id="DesktopFolder" />
<Directory Id="FontsFolder" />
<Directory Id="ProgramFiles64Folder" >
<!-- The "ID" is the name internally that refers to the folder when
trying to install files, etc... the "Name" is what the actual name
will be at actual install time as viewed by Windows Explorer -->
<Directory Id="INSTALLDIR" Name="AMYAPP" >
<Component Id="C_HKLM_AMyAppUsage" Guid="{GUID2}">
<RegistryKey Root="HKLM" Key="SOFTWARE\AMyApp" >
<RegistryValue Name="IsSomeKey" Type="string" Value="YES" KeyPath="yes" />
</RegistryKey>
</Component>
<Directory Id="Java64BitDIR" Name="Java64Bit" >
<Directory Id="Java64BitLibDIR" Name="Lib" />
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id="AMyApp_Features"
Title="AMyApp (Wix Testing)"
Level="1"
ConfigurableDirectory="INSTALLDIR" >
<ComponentRef Id="C_HKLM_AMyAppUsage" />
<ComponentRef Id="AMyApp_CORE" />
<ComponentRef Id="AMyApp_Shortcuts" />
<ComponentRef Id="JAVA64BIT_Support" />
<ComponentRef Id="JAVA64BITLIB_Support" />
</Feature>
<UI>
<UIRef Id="WixUI_InstallDir" />
<!-- Skip license dialog -->
<Publish Dialog="WelcomeDlg"
Control="Next"
Event="NewDialog"
Value="InstallDirDlg"
Order="2">1</Publish>
<Publish Dialog="InstallDirDlg"
Control="Back"
Event="NewDialog"
Value="WelcomeDlg"
Order="2">1</Publish>
<Publish Dialog="InstallDirDlg"
Control="Next"
Event="NewDialog"
Value="VerifyReadyDlg"
Order="2">1</Publish>
<Publish Dialog="VerifyReadyDlg"
Control="Back"
Event="NewDialog"
Value="WelcomeDlg"
Order="2">1</Publish>
</UI>
<Property Id="LAUNCHAPPONEXIT" Value="1" />
</Product>
</Wix>
Fragment file for the Java Subfolders sample
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<?include AMyApp_Define_Paths.wxi ?>
<DirectoryRef Id="Java64BitDIR">
<Component Id="JAVA64BIT_Support" Guid="{GUID1a}">
<File Id="_64_javax.comm.properties" Source="$(var.Path_Java64Bit)\javax.comm.properties" />
</Component>
</DirectoryRef>
<DirectoryRef Id="Java64BitLibDIR">
<Component Id="JAVA64BITLIB_Support" Guid="{GUID2a}">
<File Id="_64_HardwireDriver.jar" Source="$(var.Path_Java64BitLib)\HardwireDriver.jar" />
</Component>
</DirectoryRef>
</Fragment>
</Wix>
Fragment file for the common stuff installed at root folder
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" >
<Fragment>
<DirectoryRef Id="INSTALLDIR">
<Component Id="AMyApp_CORE" Guid="{GUID1c}" Win64="yes">
<?include AMyApp_Define_Paths.wxi ?>
<RegistryKey
Root="HKLM"
Key="SOFTWARE\AMyApp"
ForceCreateOnInstall="yes"
ForceDeleteOnUninstall="yes" >
<RegistryValue Type="string" Name="InstallDir" Value="[INSTALLDIR]"/>
<RegistryValue Type="string" Name="MyAppContext" Value="Testing"/>
</RegistryKey>
<RemoveFile Id="RemoveAllMyAppFiles" Name="*.*" On="uninstall" />
<RemoveFolder Id="INSTALLDIR" On="uninstall" />
<File Id="MyApp.exe" Source="$(var.Path_MyAppReleaseFolder)\MyApp.exe" />
</Component>
</DirectoryRef>
</Fragment>
</Wix>
Here is a simplified work-around I came up with, but don't know why it does what it does under the hood, but works.
During the declaration of the <UI> segment, I just changed both "Welcome" and "MaintenanceWelcome" dialogs to both go to the "Install Directory" dialog. It apparently bypasses the route for "Repair" / "Uninstall" but WILL allow the installer to handle it's own delete of original and re-publish the new files from the installer.
<UI>
<UIRef Id="WixUI_InstallDir" />
<!--<Property Id="WixUI_Mode" Value="InstallDir" />-->
<!-- Skip license dialog -->
<Publish Dialog="WelcomeDlg"
Control="Next"
Event="NewDialog"
Value="InstallDirDlg"
Order="2">1</Publish>
<!-- Send the MAINTENANCE Dialog to the same Install Dir dialog -->
<Publish Dialog="MaintenanceWelcomeDlg"
Control="Next"
Event="NewDialog"
Value="InstallDirDlg"
Order="2">1</Publish>
...
</UI>
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" />