WIX installer root directory and versioning - wix

I have created a silent installer Using WIX for my application. I want it to install my application to C:\MyApps folder but its Directory Id='TARGETDIR' Name='SourceDir' tag randomly picks C or D drive. I want to enforce my installation to C drive only. Also in case, I provide version number greater than 4.0.5, I am geting an error during installation saying "This installation package cannot be installed by the Windows Installer Service. You must install a newer version of the Window Installer service." I am having Windows XP professional SP3 Version 2002.

To begin with, I think you should start your WiX journey with the tutorial available here. It contains the answers to the most of basic questions you'll face with the first thing. You should also be aware that understanding WiX means understanding the concepts of Windows Installer first - otherwise some points will seem a weird magic to you.
When you create a new WiX setup project in Visual Studio, it generates a template with some placeholders. It is recommended to start modifying this template. For instance, the directory structure:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
<!-- TODO: Remove the comments around this Component element and the ComponentRef below in order to add resources to this installer. -->
<!-- <Component Id="ProductComponent" Guid="ba7d579f-5234-4448-b880-109f589d58e5"> -->
<!-- TODO: Insert files, registry keys, and other resources here. -->
<!-- </Component> -->
</Directory>
</Directory>
</Directory>
This snippet defines the INSTALLLOCATION folder under the ProgramFileFolder, and this is a better approach than to place it under the C:\ root. You can still change the installation location by modifying the INSTALLLOCATION property at install time (for instance, base on user's input).
The quick answer to your questions are:
...randomly picks C or D drive...
That's expected - it picks the drive with the most free space by the time of installation. If you stick to the way WiX template defines by default, it will fall under C: (actually, under Program Files folder).
...You must install a newer version of the Window Installer service...
Basically, it means what it says - the version Windows Installer on your machine is lower than the one you require in your package. If you try to solve the above problems with this change, then it has nothing to do with the Windows Installer version. You should require higher version than it is specified by default only in case you are going to use new features of Windows Installer.
Hope you'll make right conclusion out of this brief intro - start with the tutorial. :-)

The problem with your versions is you're changing the Windows Installer version when you change your Product version.
<Package
Id='*'
InstallerVersion='406'
Compressed='yes'
Description="Installer Number 406" />
The InstallerVersion attribute should be the minimum required version of Windows Installer required to install this package. You have Windows Installer v4.5 installed. When this is set to 406, it looks for Windows Installer v4.6 which frankly, doesn't exist. Setting this to 301 (version 3.1) is usually sufficient.
InstallerVersion='301'
While your description attribute is fine, I would find the following more meaningful:
Description="My Product v4.0.6 Installer"

Try this:
<Fragment>
<Property Id="_BrowseProperty" Value="INSTALLDIR" Secure="yes"/>
<CustomAction Id="SetDataLocationDefault" Property="INSTALLDIR" Value="[WindowsVolume]$(var.Title)\" />
<InstallUISequence>
<Custom Action="SetDataLocationDefault" After="CostFinalize" />
</InstallUISequence>
<InstallExecuteSequence>
<Custom Action="SetDataLocationDefault" After="CostFinalize" />
</InstallExecuteSequence>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR" Name="$(var.Title)">
<!-- TODO: Insert your components here. -->
</Directory>
</Directory>
</Fragment>
I think this should work!

Do not rely on TARGETDIR, and use the custom property, like this:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLLOCATION" Name="SetupProject1">
<!-- TODO: Insert your components here. -->
</Directory>
</Directory>
The template is taken from Yan's answer. Set INSTALLLOCATION to the desired folder C:\MyApps, that should do the trick.

Related

How to avoid having two versions of a product installed with Windows Installer / MSI?

I have written and am maintaining a number of Wix installation source files which I use to build MSI files for distribution of my application.
I have not explicitly programmed for any kind of upgrading, updating, reinstallation or anything of the kind -- there is a single feature that consists of a number of components with stable GUIDs and I have observed that at least a clean installation does what I expect it to.
However, I (and anyone in possession of the MSI files I distribute) may seemingly install distinct versions of my application side-by-side using their respective (distinct) MSI files. Which isn't a problem in itself, except that I obviously use the same folder as installation target -- "%ProgramFiles(x86)%\Foobar" -- to install the application (version regardless) in. Meaning that in effect there is always ever one version that ends up being installed.
I would argue that Windows Installer behaves correctly in so far that it updates files from whichever MSI package it installed last. One interesting side-effect of this is that if the last MSI was of earlier version, the files in the application folders would be overwritten with the copies from that earlier version.
But none of that seems to be the actual problem to me. I want to fix the disparity between what is actually installed (a single application version) and what Windows tracks as installed -- in my case two records of two distinct application versions.
Since I install the application in a folder that doesn't depend on the version being installed, tracking multiple application versions by Windows is a mistake.
So I guess my question is, how do I fix this so that only one version is shown (reflecting reality) or what's the idiomatic approach in these kind of cases? I deliberately did not overspecify my Wix source code, hoping in return that Windows Installer would use some built-in intelligence to figure everything out on its own. But I may need to add some explicit upgrade or uninstall-previous-version-first instructions, I suppose.
My minified Wix source code (file "foobar.wxs") would look like this:
<?xml version="1.0" encoding="utf-8" ?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi" xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product Name="Foobar" Manufacturer="ACME Inc." Id="*" UpgradeCode="ae9a7d6d-6c2d-446a-97d9-9dbe829d2ea8" Language="1033" Codepage="1252" Version="!(wix.PRODUCT_VERSION)">
<Package Id="*" Languages="1033" SummaryCodepage="1252" Compressed="yes" InstallerVersion="200" />
<Icon Id="foobar" SourceFile="!(wix.APPPATH)/foobar.ico" />
<Property Id="ARPPRODUCTICON" Value="foobar" />
<Property Id="ARPCOMMENTS" Value="Gives you full foobar powers" />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="DesktopFolder" />
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLDIR" Name="Foobar" FileSource="!(wix.APPPATH)">
<Component>
<File Id="foobar.exe" Name="foobar.exe" />
</Component>
<!-- There are other components like above (assets) -->
</Directory>
</Directory>
<Directory Id="ProgramMenuFolder">
<Directory Id="foobar_menu" Name="Foobar">
<Component Id="foobar_shortcut" Guid="e80a6b95-a145-453a-b327-65a977e741fe">
<Shortcut Icon="foobar" Id="foobar_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
<Shortcut Directory="DesktopFolder" Icon="foobar" Id="foobar_desktop_shortcut" Name="Foobar" Target="[foobar]foobar.exe" />
<RegistryValue KeyPath="yes" Root="HKMU" Key="Software\[Manufacturer]\[ProductName]" Type="string" Value="" />
<RemoveFolder Id="remove_foobar_menu" On="uninstall" />
</Component>
</Directory>
</Directory>
<Directory Id="CommonAppDataFolder">
<Directory Id="app_data_foobar" Name="foobar">
<Component Guid="" Id="app_data_config_folder">
<CreateFolder />
</Component>
<Component Guid="" Id="app_data_config_folder_log_file">
<File Name="foobar.log" Source="foobar.log.template">
<!-- Add write access permission to the log file to members of "Users" group. -->
<!-- PermissionEx Sddl="D:AR(A;;GWGR;;;BU)" / -->
<!-- Bug with Windows Installer, can't use PermissionEx/MsiLockPermissionsEx table. See https://stackoverflow.com/questions/55145282/how-to-include-inherited-permissions-when-specifying-permissions-for-a-file-inst -->
<util:PermissionEx Append="yes" GenericWrite="yes" User="Users" />
</File>
</Component>
</Directory>
</Directory>
</Directory>
<Feature Id="foobar">
<ComponentGroupRef Id="foobar" />
<ComponentRef Id="foobar_shortcut" />
<ComponentRef Id="app_data_config_folder" />
<ComponentRef Id="app_data_config_folder_log_file" />
</Feature>
</Product>
</Wix>
I am compiling the object file with the following Windows Command Prompt line:
candle.exe -ext WixUtilExtension -out %TEMP% foobar.wxs
And then generating the MSI file with:
light.exe -ext WixUtilExtension -spdb "-dAPPPATH=%apppath%" "-dPRODUCT_VERSION=%version%" -out %TEMP%\foobar-%version%.msi %TEMP%\foobar.wixobj
(using Wix 3.11.1.2318)
Upgrade Code: As long as you have set an upgrade code (which identifies a bunch of related products) you can use a major upgrade element to indicate products that are to be uninstalled as part of a new MSI's installation.
MajorUpgrade Element: Just inject a MajorUpgrade element for default treatment of major upgrades into your existing WiX source. It is a sort of "magic element" doing a lot for you making a number of (usually good) assumptions. There are older and more flexible ways to do it - if you need more detailed control (for legacy purposes usually - auto-magic does not cover all bases):
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
The above is the standard use for all WiX files created in Visual Studio.
Note: I will try to tune up this answer shortly with more links, but give that a go first?
First link: Using Visual Studio to make WiX files. The Hello WiX and Visual Studio-type of scenario.
Major Upgrade Suggested Reading: A few things to know about major upgrades. All WiX markup essentially revolves around the compiled MSI's Upgrade table. It is there that major upgrade logic is configured. Custom actions could also affect things, and a few other things such as launch conditions perhaps.
WiX Documentation: How To: Implement a Major Upgrade In Your Installer
Major Upgrade - Common Problems: WIX does not uninstall older version
Major Upgrade - Manual Configuration: Adding entries to MSI UpgradeTable to remove related products (using old-style Upgrade elements)
Further:
Major Upgrades - How-To & Concept: Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs

Deploy BizTalk application msi (created with BTDF) via msiexec

We are setting up deployment automation for our BizTalk MSIs that were created with BTDF.
Currently the approach we're working on is by calling msiexec from our deployment toolset. However, we need to install the application in a different folder than the MSI is set to propose.
The BTSF WiX default to C:-drive, whereas we must install to the E: drive. I have tried changing that in the msiexec call, but when passing INSTALLDIR or TARGETDIR, it just throws the msiexec help dialog in my face.
So I figured I should try and take a look at WiX, so that we could build the MSI to default to E: (if available), which would ultimately have the same end result, but WiX is a bit of mystery to me and it seems to have a lot of 'magic' for my understanding at this point.
BTDF by default uses the below structure (and mainly the lower portion of it), and I figure I would have to do something with the TARGETDIR and/or SourceDir. But I can't put my finger on which part is just some kind of variable that can be set.
<Directory Id="TARGETDIR" Name="SourceDir">
<?if $(var.CreateStartMenuShortcuts) ~= True?>
<Directory Id="ProgramMenuFolder">
<Directory Id="BizShortCuts" Name="$(var.ProductName) $(var.ProjectVersion)">
<Directory Id="BizShortCutsTools" Name="Deployment Tools" />
</Directory>
</Directory>
<?endif?>
<Directory Id="ProgramFilesFolder" Name="ProgramFiles">
<Directory Id="ProductDir" Name="$(var.ProductName)">
<Directory Id="INSTALLDIR" Name="$(var.ProjectVersion)"/>
</Directory>
</Directory>
</Directory>
Edit 20180129
Note that this problem occurs in a server environment, with restricted security for my logged in user. We are permitted to run msi installers (right click, custom option 'Run as [authorized user name]', with the msi UI.
In order to accomplish this via command line, I've launched as PS terminal under that other account, which works up until the point where I add the INSTALLDIR parameter. Then it simply displays msiexec help.
I doubt it makes a difference, but local version of msiexec (which works) is 5.0.7601.23593, and serverside (which doesn't work) is 5.0.9600.18333 (i.e. more recent).
First, upgrade to the Deployment Framework for BizTalk v5.6 or newer.
Second, in your .btdfproj, add DefaultInstallDir:
<PropertyGroup>
<!-- existing MSI properties -->
<ProductUpgradeCode>GUID-HERE</ProductUpgradeCode>
<!-- add DefaultInstallDir -->
<DefaultInstallDir>E:\MyCustomPath</DefaultInstallDir>
</PropertyGroup>
The MSI will now default to E:\MyCustomPath.

How to find previously installed location

I am using Wix Burn to upgrade our program with some prerequisites. The program needs to be installed in the same location that previously installed. How can I find the installed location? I am thinking about to find it from the registry Uninstall folder, however, sometimes it is not even there. Even if it is there, how do I get it from Burn?
You can use AppSearch to locate components installed by the previous installation. You can then set the installation directory dynamically by using the results of that ComponentSearch. All of this is done in the msi itself, burn is not involved in that process. The only thing you have to do with the burn bundle is to change the version number and to make sure that the UpgradeCode of the previous bundle matches the UpgradeCode of the current bundle. Bear in mind that, unlike in msi packages, the last part of the version number of a burn bundle is significant. You will also need to change the version number of the msi.
To make this work, it is necessary the previous bundle installed a component of which the GUID is known to you, and that component needs to reside inside the folder you want to upgrade.
<!-- ComponentSearch -->
<Fragment>
<Property Id="PREVIOUS_INSTALL_FOLDER">
<ComponentSearch Id="MyComponentSearch" Guid="KNOWN_GUID_OF_PREVIOUSLY_INSTALLED_COMPONENT" />
</Property>
</Fragment>
<!-- Increment Product Version, Reference Component Search, Set Target Location -->
<Product
...
Version="INCREMENTED_VERSION_NUMBER"
UpgradeCode="HAS_TO_MATCH_PREVIOUSLY_INSTALLED_UC">
<SetProperty
Id="DYNAMIC_PROGRAM_LOCATION"
Value="[PREVIOUS_INSTALL_FOLDER]"
After="AppSearch" Sequence="ui" Action="SetDynamicProgramLocation">
<![CDATA[PREVIOUS_INSTALL_FOLDER]]>
</SetProperty>
</Product>
<!-- Folder Layout -->
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="DYNAMIC_PROGRAM_LOCATION" />
</Directory>
</Directory>
</Fragment>

Wix - third party software reinstalls everytime app is run

My application installs the DesKey Dk2 dongle drivers if they are not installed or are lower than a perticular version. One of the user is complaining that whenever he runs the application from start menu shortcut, DK2 installation starts and then application is launched. Nobody else is facing this issue.
I am indtalling Dk2 as custom action in Wix script as below. lets say DK2 is represented as ABC:
<DirectoryRef Id="TARGETDIR">
<Directory Id="ABCRedistDirectory" Name="ABCDrivers">
<Component Id="ABCRedist" Guid="*">
<File Id="ABC_EXE" Source="$(var.TargetDir)ABC.exe" KeyPath="yes" Checksum="yes"/>
</Component>
</Directory>
</DirectoryRef>
<Property Id="DK2_VERSION">
<RegistrySearch Id="Dk2_Version"
Root="HKLM"
Key="SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\DESkey DK2 Uninstall"
Name="DisplayVersion"
Type="raw" />
</Property>
<Feature Id="ABCRedist" Title="ABC drivers" AllowAdvertise="no" Display="hidden" Level="1">
<ComponentRef Id="ABCRedist"/>
</Feature>
<CustomAction Id="InstallDK2Drivers" FileKey="ABC_EXE" ExeCommand="" Execute="deferred" Impersonate="no" Return="check"/>
<InstallExecuteSequence>
<Custom Action="InstallDK2Drivers" Before="InstallFinalize">
<![CDATA[NOT DK2_VERSION OR DK2_VERSION < "7.34.0.57"]]>
</Custom>
</InstallExecuteSequence>
The warning in Windows Event viewer points to main executable, which is below:
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="FolderName">
<Directory Id ="MyFolder" Name="MyApp">
<Component Id ="MyApp.exe" Guid="*">
<File Id="MyApp.exe" Source ="$(var.TargetDir)MyApp.exe" KeyPath="yes" Checksum ="yes" />
<Shortcut Id="MyAppStartMenuShortcut" Name="My App" Directory="ProgramMenuDir" Icon="MyAppIcon.exe" WorkingDirectory="MyFolder" Advertise="yes"></Shortcut>
<Shortcut Id="MyAppDesktopShortcut" Name="My App" Directory="DesktopFolder" Icon="MyAppIcon.exe" WorkingDirectory="MyFolder" Advertise="yes"></Shortcut>
</Component>
...
Now it is not happening on other machines so I am not able to diagnose. Can anybody point out any obvious mistake? What can I do to diagnose this on customer's machine i.e. how to get logs when installing .exe, .dll, some third party installers like VC100 CRT and VC100 MFC and .Net 4.0 bootstrapper?
Thanks in advance.
I can't tell from your post if the 3rd party ABC product that you're using is the same one that is repairing, or maybe the client doesn't know and you're assuming that it is your ABC thing. There's no indication in that WiX fragment exactly how you're installing the ABC thing, all you show is that it's copied to disk, there's no clue how you are running it to get it installed, and no shortcuts either.
What's happening in general seems to be that the other product is going into repair mode. There should be MsiInstaller entries in the Application Event Log that say something about whatever is wrong, referencing component ids, products, and maybe file names or registry entries.
Your setup may have a conflict with that other install. It's unlikely to be anything to do with your shortcut except that your shortcut is advertised, so it goes off into a component feature check, and is apparently finding that you are sharing something with that other app, and now it needs repairing. If the 3rd party app that repairs is not your ABC thing then you won't be able to reproduce the issue unless you also install that 3rd party thing and find out what you're sharing with it, perhaps in the wrong way.
Is this some sort of bit-torrent component? I see an abc project on sourceforge.net with Python dll's, some visual C++ runtimes included as well as other stuff. This is by no means a properly constructed package, and it could trigger the self-repair problems seen on that particular computer.
To debug this would require to see what entries exist in the Windows Event log - it will specify what MSI component triggered the repair. The error could still be in other packages since a COM call from the embedded components in this abc package, could trigger a repair in any MSI package installed on the system. As could taking over a file association associated with torrent files.
Right click My Computer
Select Manage
Event Log -> Windows Logs -> Programs
In this log you will find entries starting with "Description: Detection of product..." with id 1001 or 1004. Here is a sample:
Event ID: 1001
Description: Detection of product "{4ED0C75A-8BC5-4520-B9C7-76968FD5677F}", feature "Test" failed during request for component "{A7B09747-E527-4E1B-AE51-323CD636210F}"
This information is enough to determine what package triggered the self-repair. Please provide this information and we can take it from there.
I extracted the above sample information from Stefan Krüger's MSI FAG at installsite.org.
You are using advertised shortcuts. In your Shortcut elements, set Advertise="no" or remove the Advertise attribute completely.
For more information, see this SO answer and msdn.

How do I install to LocalAppData folder?

Following directory setting works perfectly for me.
<Directory Id='TARGETDIR' Name='SourceDir'>
<Directory Id="ProgramFilesFolder">
<Directory Id='INSTALLDIR' Name='MyApp'/>
</Directory>
</Directory>
However, when I tried changing "ProgramFilesFolder" to "LocalAppDataFolder", I got lots of error when using light to link and generate my msi:
D:\runGroup.wxs(53) : error LGHT0204: ICE38: Component cmpA5561BE36D80EB58252E69DDA0C2FF8C installs to user profile. It must use a registry key under HKCU as its KeyPath, not a file.
D:\main.wxs(38) : error LGHT0204 : ICE64: The directory INSTALLDIR is in the user profile but is not listed in the Remove File table.
Looks like "LocalAppDataFolder" is not acceptable for WiX, while I believe it is one of the system folder properties which defined in here.
What am I supposed to use for LocalAppData folder?
I converted an application from being a perMachine install to be a perUser install. In order to properly convert the install I had to add a registry key for each of the components I have.
Originally I had the following:
<Component Id="C.MyExe">
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)" Source="$(var.MyExe.TargetPath)" DiskId="1">
<Shortcut Id="SC.StartMenu"
Directory="D.ApplicationMenuDir"
Name="$(var.AppName)"
WorkingDirectory="INSTALLDIR"
Icon="MY_ICON.ico"
IconIndex="0"
Advertise="yes"
/>
...
When I moved the exe component to the user install I had to do something like this:
<Directory Id="LocalAppDataFolder" Name="AppData">
<Directory Id="MyAppDirectory" Name="$(var.AppName)">
<Component Id="C.MyExe" Guid="{MY_GUID}">
<CreateFolder />
<RemoveFolder Id="RemoveMyAppDirectory" On="uninstall" />
<RegistryKey Root="HKCU" Key="Software\MyCompany\MyApp">
<RegistryValue Name="MainExe" Value="1" KeyPath="yes" Type="integer" />
</RegistryKey>
<File Id="Fi.MyExe" Name="$(var.MyExe.TargetFileName)"
Source="$(var.MyExe.TargetPath)" DiskId="1" Checksum="yes">
</File>
</Component>
...
The most important part is that you will have to add a registry key which points to HKEY_CURRENT_USER. I added a registry value for each component which indicates that the component is installed.
I also had to remove the following: Advertise="yes".
I had this problem recently. I wanted to convert my installer from per-machine to a per-user but was getting ICE38. I asked on wix-users and one opinion was that you can ignore ICE38 because that was meant as a check for per-machine installs.
See the discussion at wix-users.
Since that is the case, ICE38 is (in my opinion) incorrect and you will want to ignore it. ICE38 implies you are installing per-user resources in the context of a per-machine installation but never verifies that this is so.
Actually authoring a per-user install requires that you ignore ICE38
because it won't ever be accurate for that world.
[Edit]
Looks like you got help here.
From Peter Shirtcliffe:
This is my own, admittedly inexpert, understanding of per-user installations:
Installing to subdirectory of LocalAppDataFolder is perfectly OK in a
per-user MSI. Because of certain scenarios relating to roaming users, you
need to add components containing elements for any
directories you create under LocalAppDataFolder. That's why ICE64 is
appearing.
The ICE38 error is slightly misleading: since you have a per-user
installation, it's safe to ignore as long as the user cannot pick an
alternative installation location that is common to all users. ICE38 is
checking for the situation where multiple users all install the same
component to the same path.
Just posting to help other people (like me).
Ok, just found that we can do it by overwriting "ProgramFilesFolder":
<SetProperty Id="ProgramFilesFolder" Value="[LocalAppDataFolder]" Before="CostFinalize"><![CDATA[NOT Privileged]]></SetProperty>
Another thing to do is, in <Package> we need to set InstallPrivileges to limited.
Well, I can see no reason why "ProgramFilesFolder" can be used directly while "LocalAppDataFolder" can't.
Are you installing per-user or per-machine? Also, what OS versions are you targetting? You might want to read:
Authoring a single package for Per-User or Per-Machine Installation context in Windows 7