Wix Installer - create installer that always re-installs over tself - wix

I am trying to create an installer that doesn't "carry" any files but does set a registry key and create a small folder structure (if not present)
The script does use CopyFile to copy files from a network location to a folder on the c-drive.
The idea is to create an installer that can be re-run whenever the network files are updated so that they are brought local for the user.
The CopyFile code has a RemoveFile line just ahead of it (in same component) so the file should always be copied "fresh"
The folder structure and registry key don't really need to be repeated but won't matter if they are.
I am also not worried about an uninstall (nor what it does as this install is always needed) but I can't create install after install in the system so I do need to be upgrading somehow.
I have (currently got) these bits of script in my wxs file
http://schemas.microsoft.com/wix/2006/wi'>
<Product
Id='*'
Name='Eclipse Template Installer'
UpgradeCode='$(var.ProductUpgradeCode)'
Version='$(var.ProductVersion)'
Manufacturer='Article 10'
Language='1033'>
<Package InstallerVersion='200' Compressed='yes' Comments='Windows Installer Package' />
<Media Id='1' Cabinet='product.cab' EmbedCab='yes' />
<Upgrade Id="$(var.ProductUpgradeCode)">
<UpgradeVersion Minimum="$(var.ProductVersion)" OnlyDetect="yes" Property="NEWERVERSIONDETECTED"/>
<UpgradeVersion Minimum="0.0.0" Maximum="$(var.ProductVersion)" IncludeMinimum="yes" IncludeMaximum="yes"
Property="OLDERVERSIONBEINGUPGRADED"/>
</Upgrade>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
</InstallExecuteSequence>
When I run the Installer for the first time, it works. Files are copied and all good.
I then tweak one of my copied files to force a new date/time on it and re-run the installer. It pops up indicating it is gathering info but doesn't then replace the file with the older version so I think the installer has concluded it needn't do anything as the current "latest version" is already installed?
If I choose Repair in Programs and Features the file does get updated.
I tried setting the IncludeMaximum to a yes but that doesn't seem to help. Same result.
I have also tried the MajorUpgrade command instead of the InstallExecuteSequence but I get the same end result. Maybe for different reasons but not really sure. Maybe not using it right.
Can anyone tell me how to force my installer to redo everything it originally did even if the version matches?
My client can't make new MSI files so updating the version and rebuilding isn't an option. They just want to update the network files (templates) and rerun the MSI to get the files local.
The first run of the MSI also configures their machine for the templates so it is all very simple, which is what they wanted.
Thanks
Simon

Might be a bad idea (I'm not sure what else this product/tool is supposed to do), but you could condition the MSI standard action "PublishProduct" to '0' so that it never runs. This way, your MSI will install and never register itself on the machine. You can continue to run your installer over and over, and it will never detect that an older version is installed.
This is, of course, not the 'correct' way to go about things, but it works.

Related

Ensure Administrator privileges for Modifying/Changing an already installed MSI?

I have a Wix project I'm modifying. It has to be installed for All Users and write to HKLM. So, in the Package I have:
<Package Id="*" InstallerVersion="405" Compressed="yes" InstallScope="perMachine"
InstallPrivileges="elevated"
Manufacturer="$(var.Publisher)"
Description="$(var.ProductName) $(var.Version) Setup" />
I also have other checks that may be working or not...
Stuff like:
<CustomAction Id='IsPrivileged' Error='You must be an admin to install this product' />
<InstallExecuteSequence>
<Custom Action='IsPrivileged' Before='AppSearch'>Not Privileged</Custom>
</InstallExecuteSequence>
What little I had, ensure that when installing or uninstalling, it would need to elevate. However, people also want to modify ... and that's when it I encountered problems.
WHen I took over, the installer had the ARPNOMODIFY set to disable modifying. Since it would be nice to be able to modify, I commented out that line:
<!--<Property Id="ARPNOMODIFY" Value="yes" Secure="yes" />-->
So, now I can "Modify" or "Change" it depending on whether it's done from the Remove Programs UI in Windows or by running the installer again.
However, it doesn't work at all. Registry entries get erased, and when run that way I never get a UAC prompt. It just tries to run the installer.
I've been at it a long time and can't figure a way to get the Modify/Change to elevate. It just goes ahead and tries to run and messes everything up. ISTM that it should ask to elevate before beginning the InstallExecute sequence, but it never does.
How can I get it to elevate or require admin privileges?
I'd prefer to find a way to elevate, but in lieu of that, I'd be happy if it was blocked from continuing. But it just wants to run and hose my installation.
You can block a repair/modify from the ARP by conditioning off the MSI property RestrictedUserControl=1. If this property is set, it means that the installer is being executed 'without' admin privileges.

WIX Installer - is there any automated way of managing Product ID / Code?

I'm developing a Windows Service installer using Wix. From what I understand, I should generate a unique Product ID for every Version of the service. This can be easily done by setting "*" in the Product Id attribute. However, when I do this and I rebuild the installer, it generated a different guid for every build. When I already have any version of service installed on my machine, this causes several problems: i.e. 'exe' file remaining after uninstall process being finished, multiple entires in Programs in Control Panel.
After some investigation, I found that the solution to this would be to update the Product Id manually every time when version number of the service would change. Or maybe I am missing something?
Anyway, the question is: is there any way to make this process automated, so that the guid would be same every time I build the same version of the service but change only when the version is updated? Any external tool for Wix?
Build Automation: You can use build automation in a number of ways to automate the creation of new product GUIDs and similar. You would pass in values to the WiX project file and such things, but perhaps that is not what you want to maintain. Let me add another option below.
Major Upgrade Element: I assume you have a major upgrade element in there? It takes care of uninstalling the previous version if configured correctly. Typically you see multiple versions in Add / Remove Programs when you re-build the same version and install on top of the last installation.
Suggestion: You can manually configure the Upgrade table to uninstall "same version installations". You take out the "auto-magic" of the "MajorUpgrade" element and do things yourself with Upgrade elements (an older way to configure the Upgrade table that yields more detailed control):
Remove this (or just comment it out):
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
Add this:
<!-- The top of your new source - compiler variables, can be overridden at compile time via candle.exe and light.exe switches -->
<?xml version="1.0" encoding="UTF-8"?>
<?define MyProductVersion = "2.0.0.0" ?>
<!-- Sample GUID only, do not use in your package ->
<?define MyUpgradeCode = "88888888-7777-7777-7777-888888888888" ?>
<..>
<Upgrade Id="$(var.MyUpgradeCode)">
<UpgradeVersion Minimum="$(var.MyProductVersion)" OnlyDetect="yes" IncludeMinimum="yes" Property="DOWNGRADE_DETECTED" />
<UpgradeVersion Property="PRODUCTLINE1" IncludeMinimum="yes" IncludeMaximum="yes" Maximum="$(var.MyProductVersion)" Minimum="0.0.0" />
</Upgrade>
<..>
<!-- You have to add RemoveExistingProducts to the InstallExecuteSequence table -->
<InstallExecuteSequence>
<!-- Potential scheduling (after): InstallValidate, InstallInitialize, InstallExecute, InstallExecuteAgain, InstallFinalize -->
<RemoveExistingProducts After="InstallInitialize" />
</InstallExecuteSequence>
Summary: The above should let you rebuild your setup and install any number of
times using the same version number and the installation process will
remove prior installation "auto-magically". Please test. No guarantees. Murphy's law. Etc... :-).
Some Links:
Adding entries to MSI UpgradeTable to remove related products
Doing Major Upgrade in Wix creates 2 entries in Add/Remove Programs
WIX does not uninstall older version (list of common major upgrade problems)

WiX MajorUpgrade of Windows Service, preserving .config, and avoiding a reboot

I am struggling to get MajorUpgrade, ServiceControl, .config files to work nicely together. After my other question, I'm now kinda having the opposite problem again.
Before, the files weren't being overwritten because the AssemblyFileVersions were static so I fixed that. 1) Now, even with Schedule="afterInstallExecute" my KeyPath='yes' .config file is still being overwritten even though the existing file modified date is different than the file creation date and it is set as a KeyPath. I'm currently having to overwrite the .config file and restart the service after the install.
2) And even if I fix that, I still have a problem of avoiding a reboot. If I say Schedule="afterInstallInitialize" then I believe the .config file will certainly be removed along with the service too early. If I say Schedule="afterInstallExecute" then the service isn't stopped and after the install a reboot is necessary. (That's right, right?) Stopping the service manually prior to the install let's me avoid the reboot. Adding a net stop custom action could work to replace the ServiceControl I guess, but getting all the conditions right seems complex.
3) As a bonus, I'd like to NOT remove the service at all during an upgrade. Can I just stop the service, replace the binary, and start the service again? That will avoid re-entering the service account credentials for an upgrade. But of course it still needs to install on a first install and uninstall on a feature removal.
Here's the meat of it (which is also bundled later, in case that somehow matters):
<MajorUpgrade DowngradeErrorMessage="A newer version is already installed."
Schedule="afterInstallExecute" />
<ComponentGroup Id="ServiceCG">
<Component Id="Service" Guid='*' Win64='yes' Directory='INSTALLDIR'>
<File Id='ServiceEXE' Source='$(var.root)Service.exe' />
<ServiceInstall Id="ServiceInstall"
Name="MyService"
DisplayName="My Server"
Type="ownProcess"
Start="auto"
ErrorControl="normal"
Description="My Server Service"
Interactive="no"
Account="[...]"
Password="[...]" />
<ServiceControl Id="StopService" Name="MyService" Start="install"
Stop="uninstall" Wait="yes" Remove="both" />
<util:User Id="UpdateServiceAccountLogonAsService" UpdateIfExists="yes"
CreateUser="no" Name="[SERVICEACCOUNTFULL]"
LogonAsService="yes"/>
</Component>
<Component Id="ServiceConfig" Guid='*' Win64='yes' Directory='INSTALLDIR'>
<File Id='FileServiceConfig' KeyPath='yes'
Source='$(var.root)Service.exe.config' />
</Component>
</ComponentGroup>
Related but unanswered:
Prevent service removal/install during WiX major upgrade - service not stopping
WiX version 3.8.1128.0
EDIT: it seems this explanation of the same issue, or on the same topic at least, might be easier to understand: Msiexec: automatic rollback to previous version on installation failure
You are banging your head against several core MSI usage problems here.
File versioning: during installation the default file overwrite mode (defined by the REINSTALLMODE property) won't replace files that are equal version by default. This can be changed by setting REINSTALLMODE = "emus". This will replace files with equal version for versioned files. Unversioned files will be preserved if modify and create dates are different.
Upgrade behavior: like Chris says, files that appear to be reverted to default are actually uninstalled and reinstalled due to major upgrade configuration. File preservation is only possible in major upgrades if RemoveExistingProducts is placed late in the InstallExecuteSequence. Then shared files between releases are never uninstalled and the file versioning rules described in point 1 apply for overwriting.
Service configuration preservation: avoiding re-entry of service credential information is a common problem for major upgrades that uninstall early in the InstallExecuteSequence. In other words the product is uninstalled and then reinstalled wiping out changed files. I don't recommend it, but some people argue for this solution: How to only stop and not uninstall windows services when major upgrade in wix? (Rob Mensching is the WIX and Orca author - I think he is suggesting this solution as an option, and not necessarily a recommendation. Please ask in the linked post to be sure). With correct component referencing and uninstall placed late in the InstallExecuteSequence this problem is normally avoided altogether, and this is then a preferred approach (normal component referencing prevent the component from uninstalling altogether leaving service settings and changed config files intact - if, and only if, component referencing is correct - see description of this concept below). However, my preferred approach is still to use a separate MSI for the service install and configuration if you are using a user account to run the service - then it is a self contained deployment unit and can be included in a bootstrapper and updated on its own, and best of all: it is undisturbed by any other application changes or hotfixes. Finally I would like to point out that a service running with a user account isn't a recommended approach to begin with - for security and deployment reasons alike.
Component referencing: refers to the GUIDs assigned to MSI components and how they must match one, and only one (absolute) path at all times through all upgrades. See a better discussion of this with a couple of examples here: Change my component GUID in wix?
I didn't mention the option of setting the MSI components installing services to permanent to prevent their removal on uninstall for the simple reason that this isn't good practice at all. Then files and registration will remain on final uninstall, and you need a custom action to clean up. Very bad practice and bound to cause lots of extra work and problems with dangling component references.
The file create/mod rule only applies to install/reinstall a component. It doesn't prevent the component from being uninstalled. Your major upgrade is scheduled very early which means the previous version is completely uninstalled and then then the new version is installed. This is why your file is overwriting when you don't expect it to. Schedule RemoveExistingProducts later to avoid this problem.
Set your Stop attribute to both install and uninstall. Those two changes should solve your problems.

MSI Major Upgrade overwriting rules

I think I read it somewhere, but couldn't find the source now and can't confirm it: when install(Major upgrade) a newer version from MSI, if the file has been modified (either by installer or user), the default rule is that old file wouldn't be replaced by the same file from a new version?
I think also I observed that behavior in the installer I wrote before, but now after a few changes, seems that it will always replace the old modified config files!
Product definition:
<Product Id="*" Name="$(var.ProductName)" Language="1033" Version="$(var.ProductVersion)" Manufacturer="Advanced Software Solution" UpgradeCode="$(var.UpgradeCode)">
<Package Id="*" InstallerVersion="200" Description="The web service installer" Compressed="yes"
InstallScope="perMachine"/>
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
Component Definition:
<Component Id='WebConfigComp' Guid='GUID'>
<File Id='WebConfigFile' Name='Web.config' Source='$(var.TheWebService.WCF.TargetBinPath)\Web.Distribution.config'
KeyPath='yes'>
</File>
</Component>
InstallExecutesequence
FindRelatedProducts 25
AppSearch 50
LaunchConditions 100
ValidateProductID 700
myScripts_CA 799
CostInitialize 800
FileCost 900
CostFinalize 1000
MigrateFeatureStates 1200
InstallValidate 1400
RemoveExistingProducts 1401
InstallInitialize 1500
BackupCA Installed 1501
ProcessComponents 1600
UnpublishFeatures 1800
SchedSecureObjectsRollback_x64 VersionNT > 400 1801
RemoveFiles 3500
RemoveFolders 3600
CreateFolders 3700
InstallFiles 4000
InstallServices VersionNT 5800
SchedSecureObjects_x64 NOT REMOVE~="ALL" AND VersionNT > 400 5801
ConfigureIIs NOT SKIPCONFIGUREIIS AND VersionNT > 400 5999
RegisterUser 6000
RegisterProduct 6100
PublishFeatures 6300
PublishProduct 6400
InstallFinalize 6600
LunchWCFReadme NOT Installed 6601
Update: I just created a new project for testing, the same behavior observed (the modified file is replaced by the newer version of installer) without change the default InstallExecSequence. Which probably means even though the File Versioning should applies, but it not actually kicked in to affect the result would expected as Remove of old version happened too early be default as Glytzhkof and PhilDW pointed out.
I am using Wix 3.8, the current stable, did I missed something?
Update2:
So far, I can confirm that moving RemoveExistingProducts after InstallFiles will keep the modified unversioned files. But the problem is that seems MajorUpgrade conflict with
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallExecute" />
</InstallExecuteSequence>
I am adding, and the error message is
Error 1 Duplicate symbol
'WixAction:InstallExecuteSequence/RemoveExistingProducts' found. This
typically means that an Id is duplicated. Check to make sure all your
identifiers of a given type (File, Component, Feature) are
unique. C:\TestDev\MySetupTest\MySetupTest\Product.wxs 5 1 MySetupTest
which is not very helpful either.
Final Update:
After digging the web thing for a while, find out what the issue is:
By default, MajorUpgrade schedules RemoveExistingProducts after
InstallValidate. You can change the scheduling using the Schedule
attribute. For example, If you choose to schedule it after
InstallInitialize, it will look like the following:
<MajorUpgrade
Schedule="afterInstallInitialize"
DowngradeErrorMessage="A later version of [ProductName] is already installed. Setup will now exit.">
Source: Wix Toolset website
So including of MajorUpgrade will indeed change RemoveExistingProducts sequence for you, which is a useful feature to have, but was unexpected for me. Thanks for all the help, now things starting make sense to me. An happy ending after all!
When a major upgrade uninstalls an existing installation before the new version gets installed (RemoveExistingProducts before InstallInitialize) it will normally remove all files that were originally installed - this includes files that may have been modified. Then the new version is installed with a fresh bundle of files.
If you schedule RemoveExistingProducts after InstallFinalize the install of the new version's files precedes the removal of obsolete files. In this scenario files are only replaced if they are versioned and newer than installed files, and for unversioned files like txt, pdf, etc... the file replacement rules basically states that the file will only be overwritten if it has not been changed on disk.
It follows that moving RemoveExistingProducts after InstallFinalize may solve your file "replacement problem" which is really a case of the modified files being deleted during uninstalland reinstalled by your current upgrade strategy.
As pointed out, your REP is early in the sequence, so it's essentially an uninstall of all the older product followed by an install of the new one. This is one type of major upgrade.
The other type of major upgrade is when REP is at the "end". In this case the new product is installed on top of the older product, and that will follow file replacement rules, that's the part that's relevant to your question.
This might be useful:
http://msdn.microsoft.com/en-us/library/aa370531(v=vs.85).aspx
and there's different rule if the unversioned files have hash checking. Yes, unversioned means "without a file version in the file's resources".
Behaviour what you are saying is unversioned file replacement logic.
Placing config file in its own component and marking the config file as the key path of the component will ensure that Windows Installer will use unversioned file replacement logic when deciding whether or not to replace this file in the install of your new version.
This post can help you to achieve the same.
I can't comment because of lack of reputation, but if you want to add
<InstallExecuteSequence> <RemoveExistingProducts After="InstallExecute" /> </InstallExecuteSequence>
and if you got duplication error, you should try this instead;
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." Schedule="afterInstallExecute" />
My guess is that the change in behavior is because you have moved RemoveExistingProducts early in the InstallExecuteSequence.
A major upgrade is essentially running an uninstall of the original product, and then a reinstall of the new one. If RemoveExistingProducts is late in the InstallExecuteSequence - and component referencing is done correctly - the existing product is not uninstall first, but its "leftover" components are removed after installing the new product. It works like a "diff" if you like. This will preserve installed files on disk that are not removed in the new version. I hope that was comprehensible - a lot at once, and rushed.
Here is a thread that deals with ensuring unversioned files get updated during an update: Forcing an upgrade of a file that is modified during its initial installation

Wix: moving <Upgrade> into <Fragment>

I'm working on a suite of related applications that all have their own WiX installer. As you can imagine, there's a lot of duplication between these installers, and I'm trying to do something about that. To this end, I've created a WiX Library Project with some .wxs files, and I've been trying to shove as much code as possible into these .wxs files. Which has worked mostly fine, so far. However, there's one thing that I can't seem to move to a .wxs: the Upgrade element.
This is my upgrade code:
<Upgrade Id="MY-UPGRADE-CODE-GUID">
<!-- If an older version is found replace it -->
<UpgradeVersion OnlyDetect="no" Property="OLDERFOUND"
Maximum="$(var.ProductVersion)" IncludeMaximum="no"/>
<!-- If the same version is found do nothing (OnlyDetect) -->
<UpgradeVersion OnlyDetect="yes" Property="SELFFOUND"
Minimum="$(var.ProductVersion)" IncludeMinimum="yes"
Maximum="$(var.ProductVersion)" IncludeMaximum="yes"/>
<!-- If a newer version is found do nothing (OnlyDetect) -->
<UpgradeVersion OnlyDetect="yes" Property="NEWERFOUND"
Minimum="$(var.ProductVersion)" IncludeMinimum="no"/>
</Upgrade>
<CustomAction Id="AlreadyUpdated" Error="This version of !(wix.Param.Upgrade.ProductName) is already installed. You will have to uninstall it manually before this installer can continue."/>
<CustomAction Id="NoDowngrade" Error="A newer version of !(wix.Param.Upgrade.ProductName) is already installed. You will have to uninstall that version manually before this installer can continue."/>
<InstallExecuteSequence>
<RemoveExistingProducts After="InstallInitialize"/>
<!-- If the same version is found execute AlreadyUpdated -->
<Custom Action="AlreadyUpdated" After="FindRelatedProducts">SELFFOUND</Custom>
<!-- If a newer version is found execute NoDowngrade -->
<Custom Action="NoDowngrade" After="FindRelatedProducts">NEWERFOUND</Custom>
</InstallExecuteSequence>
For Upgrade/#Id, I use the same value as for Product/#UpgradeCode. (I'm not sure whether that's necessary, or even good practice.) I'd like to put that in a WixVariable at some point but for now I'm trying to make it work with literal GUIDs.
I have already moved the CustomActions and the InstallExecuteSequence to a Fragment in my .wxs file. I have defined a WixVariable to contain the ProductName, so I can have each app's installer display its own name. This works exactly the way I want and expect.
However, when I move the Upgrade element into a Fragment (it doesn't matter if it's in the app's Product.wxs file or in one of the library .wxs files, and it also doesn't matter if the Fragment is the same one that contains the InstallExecuteSequence), the following problems occur:
If I first run the old version of the installer, and then the new one with Upgrade moved into a separate Fragment, but with identical Product/#Version, Product#/UpgradeCode, and Upgrade/#Id, I end up with 2 installs of the same product, which is obviously not what I want.
If I install the new installer twice, it won't install the second time (which is what I want), but it won't show my error message either.
I would replace the whole thing with a MajorUpgrade, were it not for the facts that 1) it doesn't seem to want to give an error message when the same version is installed again, and, more importantly, 2) it's not allowed inside a Fragment for some reason, so I'm still stuck with duplication (albeit a lot less than previously).
So ... how can I move my Upgrade element into a .wxs library without losing the current functionality? What am I doing wrong?
As described in the wix Linker documentation, the linker will follow references between fragments while building a windows installer database. But this means that it will ignore any fragments that aren't referenced (directly or indirectly via other fragments) by the product wxs.
This behavior can be quite useful in your scenario, because it allows you to model the dependencies between all your reusable components, and the linker will make sure to include only the ones that are needed for a specific product.
To make sure that your upgrade logic is included, you have two alternatives:
Add a dummy Property to your fragment, and reference it with a PropertyRef in your product wxs.
Or put your upgrade logic in an Include element, and then instruct the wix preprocessor to include it in your product wxs. See the Wix Preprocessor documentation for an example.