CONTEXT: I created a Bootstrapper installer in order to install, if needed, the .net framework 4.8. together with the setup program I want to deliver with it.
Up to here all works nice: my program gets installed always and the .net just if needed.
The thing is that I need to add some more checks to this installer, for example check for a registry key (if another program proper version is installed) and if this does not meet the condition, then the complete installation should be aborted, nothing should be installed.
PROBLEM: the InstallCondition I have added affects just the MsiPackage but the rest of the installation seems to be considered as totally fine and installation finishes successfully, here the interesting part of code:
<util:RegistrySearch Id="OtherProgramVersionId" Root="HKLM" Key="SOFTWARE\XXX\Install::Version" Variable="OtherProgramVersion"/>
<Chain>
<PackageGroupRef Id="NetFx48Redist" />
<MsiPackage
Id="MySuperProgram.Setup"
SourceFile="$(var.MySuperProgram.Setup.TargetPath)"
InstallCondition="OtherProgramVersion >= v10.0"/>
</Chain>
As said before, even the registry key is not found or it does not fulfill the condition, the installation seems to continue "successfully" and I get it in the ControlPanel->Programs as installed... but the main .msi was not really installed! (checking the destination folder, it's empty)
QUESTION: How can I add a global condition in order to stop completely any installation at all and show the user a message with the condition not fulfilled? If possible with a standard dialog.
I have seen (and I am still experiencing) with conditions, but seems they affect just one of the items in the chain... or they seem to break the installation somehow, I have tried adding to the .msi setup creation, file Product.wxs, the condition in order to abort this installation, but when installing I get this not passed condition as a setup error, seems the exit is not clean at all... even able to see the log where I see something like this:
Error 0x80070643: Failed to install MSI package.
Thanks in advance!
If you're using WixStandardBootstrapperApplication, you can use bal:Condition to define bundle-level conditions. The WiX documentation has a sample: https://wixtoolset.org/documentation/manual/v3/howtos/redistributables_and_install_checks/block_stdba_install_on_reg.html
Related
I've added the following to my WIX template to prevent installation without entering values in a custom dialog i've made.
<Condition Message='This installation can only run in full UI mode.'>
<![CDATA[UILevel = 5]]>
</Condition>
When I try to uninstall the application I get this message, and I'm unable to proceed.
How do I fix this so that it does not apply on uninstall?
How can I forcibly uninstall this application?
Question 1: LaunchCondition
LaunchConditions must always evaluate to true for the setup to be able to install / run. There are some further details here: Failing condition wix (recommended for more context). When you invoke uninstall via Add / Remove Programs it will run the installer in silent mode (I believe UILevel = 2 or UILevel = 3), which fails your LaunchCondition since UILevel is not equal to 5.
OR Installed: A common technique to prevent LaunchConditions to trigger problems in other installation modes than fresh install, is to add OR Installed to the LaunchCondition in question. This will force the LaunchCondition to be true for all situations and modes where the product is already installed (modify, uninstall, repair, etc...).
So something like this could probably work as an updated condition:
Installed OR UILevel = 5
Wrong Approach?: With that said I would rather implement a check to determine if the value you need specified has been set on the command line via PUBLIC properties for a silent install, instead of that rather strange LaunchCondition checking the setup's GUI level. You can still implement this as a LaunchCondition - or use a custom action for more flexibility. The LaunchCondition would check for values for all critical setup parameters, and you would prevent them from running on uninstall and other modes with the OR Installed mechanism. Here is an answer on the topic of silent installation, transforms and public properties: How to make better use of MSI files (silent deployment is crucial for corporate deployment and software acceptance).
Question 2: Forcibly Uninstall
UPDATE: A couple of additional options listed towards the bottom for completeness.
2.1 - ARP Modify: I want to run the simplest option by you before going into too much crazy detail. Is the Modify option available for your setup in Add / Remove Programs? If so, please click it and see if you then can select remove from the setup's Modify dialogs. This should work (since you are generally not running the setup in silent mode when choosing Modify).
2.2 - Interactive msiexec.exe Uninstall Command: I forgot to add that you should be able to kick off an interactive uninstall via command line as follows: msiexec.exe /x {PRODUCT-GUID} /qf. Here is how you can find the product GUID: How can I find the product GUID of an installed MSI setup? So in summmary: you find the product GUID as explained in the link, and then you open a cmd.exe window and fire off the uninstall command indicated above.
2.3 - Microsoft FixIt: If the first option above is not available, there are several other options that could work, but before trying them I would recommend giving the Microsoft FixIt tool for installation / uninstallation problems a chance to see if this does the trick for you. Run it, select your installation and see if some auto-magic is there for you to get it uninstalled.
2.4 - Advanced (avoid if you can) - hack system-cached MSI: This answer will be the next step, if the above fails: I screwed up, how can I uninstall my program? Please let us know if the above does not work, and we will check the options here. I would just zip up the cached MSI and disable the launch condition, but this is way too hacky for comfort if you can avoid it.
UPDATE: The below was added, but not needed to solve the problem. It is not recommended, it is the last resort. Leaving the content in.
Finding Cached MSI: you can find the system cached MSI using Powershell as explained here. I will inline the Powershell command here:
gwmi -Query "SELECT Name,LocalPackage FROM Win32_Product WHERE
IdentifyingNumber='{PRODUCT-GUID}'" | Format-Table Name,
LocalPackage
You then open the cached file (make a backup of it first, or zip it) with Orca or an equivalent tool, and you make whatever change needed to get the uninstall to function correctly. This is not generally considered a sane approach - it is the last resort. And what you change in the MSI is different depending on what is wrong with it. This requires specialist MSI knowledge. It is easy to mess things up so uninstall becomes even more difficult.
I just saw you got the product uninstalled whilst writing this. Puh! Be glad you don't need this latter approach. I think I will commit it and set it to strikeout so it is visible but not recommended (if only for myself to reuse if needed).
UPDATE, some additional alternatives (not always applicable, included for reference and potential re-use): 1) If you have access to the original MSI used to install your software (it must be the exact copy of the MSI used to install), then you can try to double click it and this should take you into modify directly. 2) You can also double click the file in the system cache folder if you no longer have the original installation MSI. 3) It might be you can hotfix the uninstall string in the registry as well to force a non-silent uninstall:
HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
HKLM\SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall
HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall
There are probably further ways. For example 4) hack a transform to apply during uninstall, 5) patch the installed MSI (if it is in the wild with lots of installs everywhere), etc...
I've created an installer that upgrades our software, but for some reason the XML configuration files (those of our software) are removed when upgrading.
This appears to happen if all features of the software are upgraded.
Our software is an archive type thing. If I install just that feature and upgrade it everything is fine.
However, if I install all services accompanying the archive and upgrade those then all configuration files (and each is in a different folder!) are gone.
As an example:
<ComponentGroup Id="AutoArchiveTool" Directory="AutoArchiverFolder">
<Component Id="C_AutoArchivingTool_Gateway_exe_config" NeverOverwrite="yes" Guid="{A62D5200-FDE0-4DA1-A04A-7FBDACEA83B2}">
<File Id="F_AutoArchivingTool_Gateway_exe_config" Source="$(var.Gateway.TargetDir)Gateway.exe.config" KeyPath="yes"/>
</Component>
... more script
</ComponentGroup>
If you log the installer you can even see that it's recognized as "never overwrite":
Disallowing installation of component: {A62D5200-FDE0-4DA1-A04A-7FBDACEA83B2} since the keyfile exists and the component is marked to never overwrite existing installations
Yet it's gone after the upgrade. What am I missing?
I've see the "Permanent" property, but that's not what I want. I want the installer to leave the config files alone during an upgrade. Not leave them (config files) after removal.
NeverOverwrite does not mean "don't uninstall" so:
a) if the component ID changes between the original install and the new one the sharing won't work as you intend and the ref count will decrement and the file will be removed, this being when the upgrade is scheduled "late", such as afterInstallExecute.
b) If the upgrade is scheduled early (such as afterInstallInitialize) all the old product is uninstalled first, then the new product is installed. You haven't said where your upgrade is sequenced, but sometimes Windows Installer screws up in an "early" upgrade: it decides that the file won't be overwritten, but fails to re-evaluate this when the install turns out to be an upgrade. In this case your upgrade will complete and the file will be missing. If you have this issue then a repair of the product will restore the file from the new version of the product (because that is the current owner of the component). This won't help.
So make sure that the component ID didn't change and your upgrade is scheduled late, such as afterInstallExecute. In addition, do the upgrade with verbose logging to verify what's going on.
Having said all that, NeverOverwrite is often used to solve a problem that doesn't exist. The file overwrite rules (that are invoked by a "late" upgrade) say that modified files won't be overwritten:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370531(v=vs.85).aspx
So if the first setup installs the file, then it gets updated by the app, then your upgrade runs it will not replace the modified file anyway, and there is no need to set NeverOverwrite.
<ExePackage Id="PackageID1" DisplayName="xxx" Compressed="yes"
SourceFile="..\xxx\MyExe.exe" Vital="yes"
InstallCommand="parameters to the exe"
UninstallCommand="parameters to the exe"/>
When I trigger the Uninstall action:
this.Engine.Detect();
this.Engine.Plan(LaunchAction.Uninstall);
this.Engine.Apply(System.IntPtr.Zero);
The exePackage does not get invoked. However, during Install, it enters the exe package with the right parameters.
Am I missing something here?
You need a DetectCondition attribute on your ExePackage element. The DetectCondition is how the Burn engine determines if the package is installed on the machine or not. Without a DetectCondition the engine will think the package is never installed so it will never need to be uninstalled. Since all executables are different you have to provide your own DetectCondition. Usually the XxxSearch elements in the util namespace are helpful to detect if your executable is installed.
Note: you can see the 'plan' in the log file and it should show the PackageID1 package being detected as 'Absent' even though it is installed.
I have written a managed wix bootstrapper using WPF. The actual installation steps requires chaining of multiple msi's/exe's and batch files.
<Chain>
<MsiPackage SourceFile="xxx"/>
<ExePackage Id="Test" SourceFile="..\TestBatch.bat" Vital="yes"/>
<MsiPackage SourceFile="yyy"/>
</Chain>
During the execution of each package, a message should be displayed (preferably from the bootstrapper UI) indicating which msi/exe package/ batch file is being executed currently.
In short, a ProgressText is needed in the bootstrapper How can I make this happen?
Another question: I do not want all the msi's to be packaged into the bootstrapper exe. This is because: Each time an msi is changed we would like to ship only the updated/modified msi and not the entire bootstrapper exe. Is there a way to do this?
Two answers, one suggestion:
To get messages back during the MsiPackages being installed, handle the BootstrapperCore.ExecuteMsiMessage event. The event args there will contain a Message that contains the data you are looking for.
To configure how the packages are compressed or not, use the Compress attribute. You can either mark the entire Bundle/#Compress='no' or mark each package Compress='no' (or 'yes' if you want to go that way).
--
Suggestion: Be sure to add DetectCondition to the ExePackages so Burn will know if the ExePackages are already present or not.
I'm using a bootstrapper to check for the existence and if needed install a set of 3rd party product installs. It then installs my product. I would like to include an uninstall shortcut for the full install and not just my product. However, to do that, I need to be able to set the product code for the bootstrapper and then reference it in my uninstall shortcut:
<ShortcutId="UninstallShortcut" Name="Uninstall My Product"
Description="Uninstalls My Product"Target="[System64Folder]msiexec.exe"
Arguments="/x [MyBootStrapperProductCode]" Icon="MainApp.ico"/>
I'm using the standard Wix bootstrapper, but I don't see anything within the Bundle element that will let me set the product code.
Alternately, can I prevent the bootstrapper from leaving references to itself in Add/Remove Programs? The 3rd party components are permanent deployments.
The Bundle doesn't work the same way as Product. It does not use msiexec to unistall, atleast not publicly.
In order to create UNISTALL shortcut for BUNDLE, you need some clever tricks. Disclaimer: only for developmental/internal use.
First; you need to pass UpgradeCode to your MSI, using this approach:
Passing command line args to MSI from WiX bundle
After that, in your MSI file, you could try searching registry value BundleUpgradeCode which equals to your UpgradeCode. If you have found the folder where value lies, you can extract UnistallString and execute it directly(using CustomAction).
It will be something like this: "C:\ProgramData\Package Cache{my GUID}\ExchangeBootStrapper.exe" /uninstall
I personally haven't implemented it yet, but couldn't find any other workaround for this problem and came up with this one.
I am late, but at least for the record. As I understand the products in the Chain of Burn are handled independently. So the uninstalls does. It means that you don't need the code for the whole bundle. Codes of the individual Msi files in the bundle chain will be used for the un-installation. As for permanent installation of 3d parties there is corresponding Permanent attribute. This all is quite well described in last two chapters of WiX 3.6 Guide by Ramirez N.