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

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

Related

ICE03: Invalid Language Id. Solution without suppressing the retrieval of file information

I have a Setup Project for WiX v3 in Visual Studio. When building I am getting the same error on two different files. I would like to avoid this error without suppressing the retrieval of file information. Ideally I want to solve this with changes to the xml to make it easier for my coworkers to make changes in the future.
ICE03: Invalid Language Id; Table: File, Column: Language
The problem with both files is that they have a language of 1252. Both are dll's from the mid 90's and published by someone else.
I have read that I can pass -sh to light.exe to suppress retrieval of file information. Or I can skip ICE03 when running light. I would rather not do that since I could miss other errors.
I have tried setting the language to 0 on the product. I have also tried setting the default language on the files themselves. There is more language related markup I tried but I didn't have any luck.
In the xml have included only the two files that are causing the error. As well as any supporting xml incase it is of interest.
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="MyProject" Language="1033" Version="1.0.0.0" Manufacturer="My Company" UpgradeCode="8156a540-97a9-4d3d-b345-9a6d8b833be9">
<Package InstallerVersion="200" InstallScope="perMachine" Platform="x86"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate/>
<Property Id="INSTALLLOCATION" Value="C:\"/>
<SetDirectory Action="SetInstallDir" Id="INSTALLDIR" Value="[INSTALLLOCATION]"/>
<Feature Id="ProductFeature" Title="MyProject" >
<ComponentGroupRef Id="Run32Components" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="INSTALLDIR">
<Directory Id="RRSDIR" Name="RRS">
<Directory Id="RUN32DIR" Name="RUN32">
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="Run32Components" Directory="RUN32DIR">
<Component Id="PICS2.DLL_Component" Guid="{855DFCC8-50B1-4213-81BA-68878EB31486}">
<File Id="PICS2.dll" Name="PICS.dll" Source="RUN32\PICS2.DLL" />
</Component>
<Component Id="PICSCCRW.DLL_Component" Guid="{A1859D86-1556-497D-8254-118823BF92FB}">
<File Id="PICSCCRW.DLL" Name="PICSCCRW.DLL" Source="RUN32\PICSCCRW.DLL"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>
16-Bit Windows Modules: These two DLL files appear to be 16-bit Windows modules, and as such the language set seems to be a code page
and not a language code at all. I will forward to the WiX guys. Just ignore the whole problem I would say, should be fine - barring the madness of installing 16-bit components :-).
Leaving the below for future reference - just in case. Real answer
ends here.
Preliminary Links: While at it, let me lob you some links to related, pre-existing answers. I assume you have found at least one of them already:
wix installer ice03 Invalid Language Id.
Wix toolset license agreement multi-languages issue
As seen in the latter answer you might have a session toying with code pages for the whole package. There are probably better fixes than that though.
Ad-Hoc Sample: Always impossible to add something that you haven't tested and have it work, but just so it is clearer without compiler variables, LCIDs set in Product and Package elements:
<Product Id="*" Name="MyApp" Language="1041" Version="1.0.0"
Manufacturer="MyCompany" UpgradeCode="PUT-GUID-HERE">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine"
Languages="1041" SummaryCodepage="932" />
LCID:
LCID
VbsEdit LCID Chart
CodePage:
Creating a Database with a Neutral Code Page
https://en.wikipedia.org/wiki/Code_page
https://en.wikipedia.org/wiki/Windows_code_page
Other Links:
wix incorrect font used during installation on Windows XP

WiX Bootstrapper

I am trying to create an installer for a simple .NET WPF C# app. I am using VS 2013, and WiX 3.10.2. Following the steps in the Wix Tutotial/ .NET/ Bootstrapping I have created a Boostrap.exe which chains .NET web installer and the app Setup.msi.
EDIT: My goal is to understand how to configure the WiX Bootstrap and Setup projects for small updates, minor upgrades, and major upgrades scenarios.
Out of the box, everything seems to work nice when I run a fresh install. However, when I run a fresh built Bootstrap.exe over an already existing installation a duplicate entry appears in the Apps & features and no file is changed in the app target location - contrary to the expectation that the same entry should remain in the Apps & features and the target location should be updated. EDIT: Looks like there may not be a way to change REINSTALLMODE?
If I add a Product Id and then change the version of the Setup (minor upgrade), the Bootstrap fails with User cancelled installation? The log file shows "Error 0x80070642: Failed to perform minor upgrade of MSI package." EDIT: Inside the MSI log a SecureRepair fails with error code 39439E438 (?) probably because the stored hash value does not match the current... but that should be expected in a minor upgrade MSI, right?
Are there recommended configurations between the Boostrapper and Setup WiX projects such that the small update, minor upgrade, and major upgrade use cases can be handled properly, or does the WiX Bootstrapper support ONLY major upgrades?
I will continue to investigate and I'll post updates to my findings;
Any hints are greatly appreciated,
Thanks!
Here are the source files which I barely changed from the WiX wizard generated code:
--- Product.wxs ---
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="*" Name="SetupProject1 1.0.0.0" Language="1033" Version="1.0.0.0" Manufacturer="Acme" UpgradeCode="4c8a8cbf-e3d0-410c-8a8d-7e67eb4e7ff7">
<Package InstallerVersion="200" Compressed="yes" InstallScope="perUser" InstallPrivileges="limited" />
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
<MediaTemplate />
<Feature Id="ProductFeature" Title="SetupProject1" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="LocalAppDataFolder">
<Directory Id="AcmeFolder" Name="Acme">
<Directory Id="INSTALLFOLDER" Name="WpfApplication1" />
</Directory>
</Directory>
</Directory>
</Fragment>
<Fragment>
<ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
<Component Id="ProductComponent" Guid="8CA0B70F-39DA-4B4B-9104-46C58E26FCF4">
<CreateFolder/>
<RemoveFolder Id="RemoveAcmeFolder" Directory="AcmeFolder" On="uninstall"/>
<RemoveFolder Id="RemoveINSTALLFOLDER" On="uninstall" />
<RegistryValue Root="HKCU" Key="Software\Acme\WpfApplication1" Name="Version" Type="string" Value="[ProductVersion]" KeyPath="yes" />
<File Source="$(var.WpfApplication1.TargetPath)" KeyPath="no" />
</Component>
</ComponentGroup>
</Fragment>
--- Bundle.wxs ---
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:bal="http://schemas.microsoft.com/wix/BalExtension">
<Bundle Name="Bootstrapper1 1.0.0.0" Version="1.0.0.0" Manufacturer="Acme" UpgradeCode="e1092cbb-9134-42fc-a9f2-652f95f361fd">
<BootstrapperApplicationRef Id="WixStandardBootstrapperApplication.RtfLicense" />
<Chain>
<MsiPackage Name="Acme Setup" SourceFile="$(var.SetupProject1.TargetPath)" Vital="yes" />
</Chain>
</Bundle>
If you change your executables, increase their version numbers. Windows Installer assumes files with the same versions are the same.
To upgrade an .msi package, increase its version number either as part of a major upgrade (typical) or minor upgrade.
To upgrade a bundle, increase its version number. By default, Burn keeps bundles with the same version installed.

WiX Conditional Feature/Component Orphaned on Uninstall

Edit: Quoting myself because I summarized the issue much better in one of the comments below…
I have a condition that is true when the package is installed, but
not true when it is removed. I expected MSI to remember that it had
installed the conditional component and remove it with the uninstall,
but this is not the case. I am trying to find out A) the proper way to
clean up this orphaned component, and B) the best way to protect
against this problem in the future.
I guess my question boils down to, is it safe to just delete an orphaned feature/component after a product is uninstalled? And is there any way to check what, if anything, is still referencing a component that I believe to be an orphan? And how do I fix my installer to keep this from happening in the future?
We have a wix project to install a library, Foo. This installer puts copies of Foo.dll into the GAC, and a folder, Program Files\Reference Assemblies\Foo\<version> by default. The installer also adds two registry keys, one is a custom key which stores the path of the Foo folder for reuse in future installs, the other tells Visual Studio to include the full <version> folder path in its search for installed libraries so that Foo shows up in the “Add References” dialog. Multiple versions of the Foo library can be installed on the machine at a time, each will be located in the appropriate <version> folder under Foo.
Foo 2.0.0 had a bug that slipped through testing, Foo 2.0.1 contained the bug fix, no other changes. It was decided that since the bug fix was the only change, we would add a policy file to the GAC which would redirected references for Foo 2.0.0 to Foo 2.0.1. This policy file was added to the installer as a new component inside of a new feature. An upgrade tag was added to detect and remove Foo 2.0.0 when Foo 2.0.1 was installed. The installation of the policy feature was made conditional on Foo 2.0.0 being detected. Everything seemed to be working and Foo 2.0.1 was pushed out.
Now, a year later, we discover that we again missed noticing a bug, this time in the installer setup rather than the library code. It turns out that when Foo 2.0.1 replaces 2.0.0, and is then uninstalled, the policy file is orphaned and remains in the GAC while all other files and keys are removed. I have tested this on a clean install of windows (virtual machines can be so useful) and confirmed that the problem can be replicated, i.e. no additional references to the component have snuck in to cause it to stay behind.
All of this was originally done in WiX 3.0 but we have recently moved up to using WiX 3.5. Our WiX code looks like this:
<Product Id="Guid 1" Name="Foo v2.0.1" Language="1033" Version="2.0.1" Manufacturer="My Team" UpgradeCode="Guid 2">
<Package InstallerVersion="300" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Upgrade Id="Guid 2">
<UpgradeVersion Minimum="2.0.0" Maximum="2.0.0" IncludeMaximum="yes" IncludeMinimum="yes" OnlyDetect="no" Property="UPGRADE2X0X0"></UpgradeVersion>
</Upgrade>
<Property Id="FOODIR">
<RegistrySearch Id="FooPath" Type="directory" Root="HKLM" Key="Software\Foo" Name="InstallPath"></RegistrySearch>
</Property>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="RefAssemb" Name="Reference Assemblies">
<Directory Id="FOODIR" Name="Foo">
<Component Id="FooLibPath" Guid="Guid 3">
<RegistryKey Root="HKLM" Key="Software\Foo" Action="createAndRemoveOnUninstall">
<RegistryValue Name="InstallPath" Type="string" Value="[FOODIR]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
<Directory Id="FOOVERSION" Name="v2.0.1">
<Component Id="Foo_VSFile" Guid="Guid 4">
<File Id="Foo_DLL" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes"></File>
</Component>
<Component Id="Foo_VSRegKey" Guid="Guid 5">
<RegistryKey Root="HKLM" Key="SOFTWARE\Microsoft\.NETFramework\v3.5\AssemblyFoldersEx\Foo v2.0.1" Action="createAndRemoveOnUninstall">
<RegistryValue Type="string" Value="[FOOVERSION]" KeyPath="yes"></RegistryValue>
</RegistryKey>
</Component>
<Directory Id="FOOGAC" Name="GAC">
<Component Id="Foo_GAC" Guid="Guid 6">
<File Id="Foo" Source="$(sys.CURRENTDIR)2.0.1\Foo.dll" KeyPath="yes" Assembly=".net"></File>
</Component>
<Component Id="Foo_Policy_2x0x1" Guid="Guid 7">
<File Id="Foo_PolicyDLL" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.dll" KeyPath="yes" Assembly=".net"></File>
<File Id="Foo_PolicyConfig" Source="$(sys.CURRENTDIR)2.0.1\policy.2.0.Foo.config" CompanionFile="Foo_PolicyDLL"></File>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
<Feature Id="ProductFoo" Level="1">
<ComponentRef Id="Foo_GAC"/>
<Feature Id="Foo_VSSupport" Level="1">
<ComponentRef Id="FooLibPath"/>
<ComponentRef Id="Foo_VSFile"/>
<ComponentRef Id="Foo_VSRegKey"/>
</Feature>
<Feature Id="Foo_Policy_v2x0x1" Level="0">
<ComponentRef Id="Foo_Policy_2x0x1"/>
<Condition Level="1">UPGRADE2X0X0</Condition>
</Feature>
</Feature>
</Product>
is it safe to just delete an orphaned feature/component after a
product is uninstalled?
No, it's not. If you just delete it, its component registration information is still left on the machine.
And is there any way to check what, if anything, is still referencing
a component that I believe to be an orphan?
Not really. But if there is something referencing one of your components, it's most likely another product developed by you or an older version of your current product which wasn't uninstalled correctly.
It's very unlikely that a random product would reference your component or assembly.
And how do I fix my installer to keep this from happening in the
future?
Use major upgrades which uninstall the old component and install the new one. No special policy files, no conditional installations or removals.
Multiple versions of the Foo library can be installed on the machine
at a time, each will be located in the appropriate folder
under Foo.
Why? If you have a single product, you can use major upgrades. This way the user will have only one version installed with only one version of your assembly.
Versioned assemblies installed side by side make sense only for different products.
It was decided that since the bug fix was the only change, we would
add a policy file to the GAC which would redirected references for Foo
2.0.0 to Foo 2.0.1. This policy file was added to the installer as a
new component inside of a new feature.
This is a hack and most likely this is what is causing the problem. Your new installed should have uninstalled the old version along with Foo 2.0.0.
Major upgrades should always be standalone.

After minimize/restore the EULA window the information disappeared

I'm using ScrollableText control to display EULA and everything works fine except the following scenario:
When EULA dialog is displayed select some word(s) then minimize the EULA window. After restoring the window whole license information disappeared. But after selecting some area in the control the EULA text is appears.
Could somebody help me to understand the root cause of the problem.
I have tried it with 3.0.5419.0 build on WindowsXP SP3 and Windows Installer 4.5 and have the same problem.
The WIX source is following:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define ProductName="WixProject"?>
<?define ProductVersion="1.0.1"?>
<?define Manufacturer="WixProject"?>
<?define Language="1033"?>
<?define ProductCode="{830E8896-AD07-4fbb-8828-4165D2C84887}"?>
<?define UpcradeCode="{BA074C59-1F12-4a95-8BD8-177E18234AB3}"?>
<Product Id='$(var.ProductCode)'
Version='$(var.ProductVersion)'
Name='$(var.ProductName)'
Language='$(var.Language)'
Manufacturer='$(var.Manufacturer)'
UpgradeCode='$(var.UpcradeCode)'>
<Package InstallerVersion="200" Compressed="yes" />
<Upgrade Id='$(var.UpcradeCode)'>
<UpgradeVersion OnlyDetect="no" IncludeMaximum="no" Property="OLD_VERSION_FOUND" Maximum='$(var.ProductVersion)' />
</Upgrade>
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="WixProject1">
<Component Id="ReadMeC" Guid="{3DC5A180-EC42-4466-8E4B-1BA37BFF189C}" SharedDllRefCount="yes" Win64="no">
<File Id="ReadMeF" Name="ReadMe.txt" Source="ReadMe.txt" Vital="yes" />
</Component>
</Directory>
</Directory>
</Directory>
<InstallExecuteSequence>
<FindRelatedProducts Sequence="200" />
<RemoveExistingProducts After='InstallFinalize' />
</InstallExecuteSequence>
<Feature Id="ProductFeature" Title="WixProject1" Level="1">
<ComponentRef Id="ReadMeC" />
</Feature>
<UIRef Id="WixUI_Minimal" />
</Product>
</Wix>
This is the very simple WIX file which installs only one file and uses WixUI_Minimal UI.
Even for this installer I can see the same problem.
Thanks.
The ScrollableText control is quite limited (as well as MSI UI in general). It's hard for me to point out the exact reason of such behavior, but probably one of Rob's suggestions helps (for instance, re-save your license.rtf in WordPad and try again).
It would also be interesting to know what exactly helped (when you manage to dig it out). :-)
All of my InstallShield installers don't even the minimize button enabled and I've never missed it. My WiX installers have minimize capabilities but I haven't reproduced your problem. What version of MSI do you have installed? Can you repro this on different machines with different versions?
I can reproduce this behavior using WiX's own installer. I guess I've just never tried to select some text then minimize the installer before. This is clearly a defect in MSI's internal UI and there isn't going to be anything you can do about it other then not enable the Minimize button like InstallShield does.
I have asked about this issue from the WIX developers and they say that the problem is in the Windows Installer RichEdit control.
For details please refer to the link below:
http://sourceforge.net/tracker/?func=detail&atid=642714&aid=3087369&group_id=105970

Upgrade individual feature in WIX feature-tree without uninstalling/upgrading other feature(s)

I'm trying to create a setup project using WIX that will allow me to install multiple features of a single product. How can I update one of the installed features (which is independent of the other installed features) without having to reinstall the other features in the feature-tree?
For example, I want to be able to have a project (going back to HelloWolrd) called HelloWolrd, which (surprise) prints "Hello world!" on the screen. Let's say that I have three of these hello world applications, Hello World 1, Hello World 2, and Hello World 3. Each of which prints on the screen Hello World 1, 2, or 3, respectfully. What I would like is to create an MSI which by default installs all three of these "features" but also allows upgrading of each feature individually at a later time.
Here is my layout of my solution:
Solution Explorer http://img12.imageshack.us/img12/5671/solutionexplorerm.jpg
My WIX Product.wxs file looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="ca484210-c719-4b2e-b960-45212d407c11" Name="HelloWorldInstaller" Language="1033" Version="1.0.0.0" Manufacturer="HelloWorldInstaller" UpgradeCode="68eeb8cb-9ef3-443c-870c-9b406129f7ff">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<!-- Create Directory Structure -->
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Hello World" />
</Directory>
<Directory Id="DesktopFolder" Name="Desktop"/>
</Directory>
<DirectoryRef Id="INSTALLLOCATION">
<Component Id="HelloWorld1" Guid="6D1D9D33-DA17-4db3-8132-C39F32200C3A">
<RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld1\Install" Action="createAndRemoveOnUninstall">
<RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
</RegistryKey>
<File Id="HelloWorld1.exe" Name="$(var.HelloWorld1.TargetFileName)" Source="$(var.HelloWorld1.TargetPath)" DiskId="1" Checksum="yes">
<Shortcut Id="HelloWorld1ApplicationDesktopShortcut" Name="Hello World 1" Description="Hello World Application 1" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
</File>
</Component>
<Component Id="HelloWorld2" Guid="B2D51F85-358B-41a7-8C45-B4BB341158F8">
<RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld2\Install" Action="createAndRemoveOnUninstall">
<RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
</RegistryKey>
<File Id="HelloWorld2.exe" Name="$(var.HelloWorld2.TargetFileName)" Source="$(var.HelloWorld2.TargetPath)" DiskId="1" Checksum="yes">
<Shortcut Id="HelloWorld2ApplicationDesktopShortcut" Name="Hello World 2" Description="Hello World Application 2" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
</File>
</Component>
<Component Id="HelloWorld3" Guid="A550223E-792F-4169-90A3-574D4240F3C4">
<RegistryKey Root="HKCU" Key="Software\HelloWorldInstaller\HelloWorld3\Install" Action="createAndRemoveOnUninstall">
<RegistryValue Name="DTSC" Value="1" Type="integer" KeyPath="yes" />
</RegistryKey>
<File Id="HelloWorld3.exe" Name="$(var.HelloWorld3.TargetFileName)" Source="$(var.HelloWorld3.TargetPath)" DiskId="1" Checksum="yes">
<Shortcut Id="HelloWorld3ApplicationDesktopShortcut" Name="Hello World 3" Description="Hello World Application 3" Directory="DesktopFolder" WorkingDirectory="INSTALLLOCATION" />
</File>
</Component>
</DirectoryRef>
<Feature Id="HelloWorld1Feature" Title="Hello World 1" Level="1">
<ComponentRef Id="HelloWorld1"/>
</Feature>
<Feature Id="HelloWorld2Feature" Title="Hello World 2" Level="1">
<ComponentRef Id="HelloWorld2"/>
</Feature>
<Feature Id="HelloWorld3Feature" Title="Hello World 3" Level="1">
<ComponentRef Id="HelloWorld3"/>
</Feature>
</Product>
</Wix>
Now, when this is built, it installs the features as you would expect. However, when you make a modification to HelloWorld1.vb and recompile, I would like it to be able to reinstall (upgrade) only that feature, not all of them.
When I update one file, and rebuild the solution, then try to install the msi, i get this error:
MSI Error http://img696.imageshack.us/img696/849/anotherversionisinstall.jpg
I updated my code to allow for uninstalling of the features and allow the use of upgrade codes, but that un-installed all of the features, and re-installed all of them.
-- Real world application --
The real world application to this is a large software package that needs multiple support applications that run as services/scheduled tasks on a regular basis. I would like to get the install of these supporting apps into one MSI allowing us to not have such a nightmare of rolling out each exe individually. I know that if we have an update to one of the exe's that we could just manually compile that exe and roll it out, but I'd like to do this in a completely reproducible manner.
Any help would be appriciated,
Thank you!
EDIT:
I added the source for download from Google Code. Thanks again!
I got this figured out and thought I would post the answer here for future reference for others. So I have fully explained the problem, I will go in to more depth of the real world scenario.
We have a moderately large piece of software that requires us to have multiple supporting applications that run on a number of different servers. Our current progression of upgrades makes it moderately difficult to upgrade code in a reliable fashion. Currently we use self extracting exe's to rollout our code to the different servers. The problem arises when we have such a large number of supporting applications that it becomes hard to make sure that the applications got installed correctly with the correct configuration settings, etc. To solve this problem we are looking into the ability to instead of compressing each of the supporting applications, we create a single installer (MSI) that will allow the infrastructure team to install a specific set of supporting applications to each given machine. When we have a major change (for example from 1.0 to 2.0) we will do a full upgrade install (meaning all services/processes will need to be stopped, un-installed, installed, and started.) When we have a minor change, we would like to only have to stop and reinstall the affected services/processes, without touching other applications. Now, enough of me rambling, lets get to the solution:
I modified the WIX Product.wxs to remove the shortcuts as we don't really need them in our scenario. Here is the updated wxs file:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Product Id="13C373D3-5C27-487e-A020-C2C89E4607B1" Name="HelloWorldInstaller" Language="1033" Version="1.0.0.0"
Manufacturer="HelloWorldInstaller" UpgradeCode="E7CB3C76-4D51-48a8-BFB4-6D11B2E2E65B">
<Package InstallerVersion="200" Compressed="yes" />
<Media Id="1" Cabinet="product.cab" EmbedCab="yes" />
<FeatureRef Id="HelloWorld1Feature" />
<FeatureRef Id="HelloWorld2Feature" />
<FeatureRef Id="HelloWorld3Feature" />
</Product>
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFilesFolder">
<Directory Id="INSTALLLOCATION" Name="Hello World" />
</Directory>
<Directory Id="DesktopFolder" Name="Desktop"/>
</Directory>
</Fragment>
<Fragment>
<DirectoryRef Id="INSTALLLOCATION">
<Directory Id="HelloWorld1Directory" Name="Hello World 1">
<Component Id="HelloWorld1Component" Guid="6D1D9D33-DA17-4db3-8132-C39F32200C3A">
<File Id="HelloWorld1.exe" Name="HelloWorld1.exe" Source="HelloWorld1.exe" DiskId="1" Checksum="yes" />
</Component>
</Directory>
<Directory Id="HelloWorld2Directory" Name="Hello World 2">
<Component Id="HelloWorld2Component" Guid="B2D51F85-358B-41a7-8C45-B4BB341158F8">
<File Id="HelloWorld2.exe" Name="HelloWorld2.exe" Source="HelloWorld2.exe" DiskId="1" Checksum="yes" />
</Component>
</Directory>
<Directory Id="HelloWorld3Directory" Name="Hello World 3">
<Component Id="HelloWorld3Component" Guid="A550223E-792F-4169-90A3-574D4240F3C4">
<File Id="HelloWorld3.exe" Name="HelloWorld3.exe" Source="HelloWorld3.exe" DiskId="1" Checksum="yes" />
</Component>
</Directory>
</DirectoryRef>
</Fragment>
<Fragment>
<Feature Id="HelloWorld1Feature" Title="Hello World 1" Level="1">
<ComponentRef Id="HelloWorld1Component"/>
</Feature>
</Fragment>
<Fragment>
<Feature Id="HelloWorld2Feature" Title="Hello World 2" Level="1">
<ComponentRef Id="HelloWorld2Component"/>
</Feature>
</Fragment>
<Fragment>
<Feature Id="HelloWorld3Feature" Title="Hello World 3" Level="1">
<ComponentRef Id="HelloWorld3Component"/>
</Feature>
</Fragment>
</Wix>
Now along with this, for our minor upgrades, we will be looking at releasing patches for our components.
For example, let's say we have a ProductA, which has three components - 1,2, and 3. These three components must run either as services, or scheduled tasks. The nature of our product, we cannot shut down all of our services to update or fix one of our components. So, if after we've installed version 1.0, we find a bug in component 2, but we don't want 1 or 3 to be affected by the fix being applied to this bug, we will be releasing a patch for component 2, thus only component 2 will be affected.
For our quick example above, we are using HelloWorld1, HelloWorld2, and HelloWorld3 as our 3 components in our software application. The thought is that we should be able to install all three with one MSI, but then update each one independently without it affecting any of the other installed components.
So, to demonstrate this, I have created the three console applications above that will display "Hello World 1!", "Hello World 2!", and "Hello World 3!". Then after we release the initial MSI, lets say we find a "bug" that requires us to have HelloWorld1 say "Hello World 1! Updated." instead. Here is what we will do to simulate this:
Create the Product.wixobj by executing this command at the command prompt:candle.exe Product.wxsPlease remember that in order to call the candle.exe or any of the WIX commands, the Wix install directory should be in your PATH variable. (Short tutorial on updating PATH environment variable) Also, please perform the commands in the same directory as your Product.wxs file.
Create the first version of your product (lets say 1.0): light.exe Product.wixobj -out ProductA-1.0.msi
Now find a bug (change the output of HelloWorld1 to say "Hello World 1! Updated.") then update the assembly version and file version. This is important as this is how WIX can tell the exe's are different.
Run the same command as step one (for good measure): candle.exe Product.wxs
Run nearly the same command as step two: light.exe Product.wixobj -out ProductA-1.1.msi Notice that this is version 1.1 instead of 1.0 (this is the msi with our updated code). However, we don't want to just install this, keep reading.
Here is the fun part, we get the difference in the two MSIs with the following command: torch.exe -p -xi ProductA-1.0.wixpdb ProductA-1.1.wixpdb -out Diff.WixMst
Now we create the patch file from this (Patch.wxs will be explained below): candle.exe Patch.wxs
We will now create the WixMsp file with this command: light.exe Patch.wixobj -out Patch.WixMsp
And now, the fun part. Create the MSP file with this command: pyro.exe Patch.WixMsp -out Patch.msp -t RTM Diff.Wixmst
Now, if everything went according to plan, you should have two msi's and one msp file. If you install the first msi (ProductA-1.0.msi) and run HelloWorld1.exe, you should see the message, "Hello World 1!". Just for fun (and example), run both the other applications and leave them running (I built in a stop to keep them open). Close HelloWorld1.exe as we are now going to apply the update for that exe, but in doing so we will not affect HelloWorld2.exe or HelloWorld3.exe. If you now install the msp (Patch.msp) file, and then run HelloWorld1.exe, you will see the updated message, "Hello World 1! Updated."
Now, for the magical Patch.wxs file:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Patch
AllowRemoval="yes"
Manufacturer="Dynamo Corp"
MoreInfoURL="http://www.dynamocorp.com/"
DisplayName="Sample Patch"
Description="Small Update Patch"
Classification="Update"
>
<Media Id="5000" Cabinet="RTM.cab">
<PatchBaseline Id="RTM"/>
</Media>
<PatchFamilyRef Id="SamplePatchFamily"/>
</Patch>
<Fragment>
<PatchFamily Id='SamplePatchFamily' Version='1.0.0' Supersede='yes'>
<ComponentRef Id="HelloWorld1Component"/>
</PatchFamily>
</Fragment>
</Wix>
Doesn't look like much, does it? Well, the most interesting parts are these:
<PatchBaseline Id="RTM"/> - This if you recall is used in our creation of the patch msi. The "RTM" is referred to in the last step above: -t RTM - These have to match.
<ComponentRef Id="HelloWorld1Component"/> - This points the patch to the correct component to update, in our case HelloWorld1Component.
If you've been doing any searching around, the above code may seem familiar because it came from Peter Marcu's Blog: WiX: Building a Patch using the new Patch Building System - Part 3
I also relied heavily on Alex Shevchuk's Blog: From MSI to WiX, Part 8 - Major Upgrade
If you're wondering, "Wow, that's a lot of steps, why would anyone do this many steps?", please remember that once the hard work (above) is done, you need to move this into your integration routine. Thats right, integrate, integrate, integrate! How do you do this? Well, thats a bit more research, and maybe a blog post? - Probably. To get you off on the right foot, here is an awesome article on Automate Releases With MSBuild And Windows Installer XML.
Wow, I hope you read all of this (all two of you), and learned a lot. I hope this helps someone other than myself.
Thank you!
Sounds like you figured out the upgrade scenario, now you just need to figure out Where to place RemoveExistingProducts in a major MSI upgrade so that features aren't reinstalled if they haven't changed :)