How to author a WiX Burn bundle including a ComPlus proxy installer - wix

We have the scenario with an application with one .Net- and one VB6-client that should be deployed as a single bundle. The VB6 part on its hand contains its binaries as well as an ComPlus proxy. The proxy's installer is generated using COMAdminCatalog.ExportApplication. The three parts is bundled together with WiX Burn and uses the standard bootstrapper application. The creation of the bundle is part of an automatic build and deploy chain.
The problem is how to handle the proxy's installer in the bootstrapper.
There is no way of controlling the product- and upgrade code or version in COMAdminCatalog.ExportApplication. Thus a previous version can't be upgraded by a new. (At least I have not found a way, is there any?)
The msi generated from COMAdminCatalog.ExportApplication does not handle repair or reinstall. Will result in error and rollback of whole bundle.
Two versions of the proxy cannot be installed at the same time. Second one will result in error and rollback.
I have had some progress wrapping the complusproxy.msi in a bat script and use ExePackage instead of MsiPackage. The script begins with removing any previous version of the complusproxy. The problem with this is to get all different install/upgrade/uninstall-scenarios to work correctly.
I'm considering the option to edit the generated complusproxy.msi to get control of the upgrade code and version and add that to the build process.
It feels like I'm trying complex work arounds and it should be a correct, better way of doing it. What would be a better or good enough approach to solve it?

Related

WiX bundle install and standalone MSI for upgrades

I have a WiX bundle that includes the .NET core runtime installer and my product MSI. If I don't tweak anything, I get two entries in the Add/Remove Programs list - one for the bundle and one for the product MSI. I have tried two approaches of showing only one but I run into some issue either way.
Approach #1 - Set MsiPackage attribute "Visible" to "no" in the bundle. This leaves a single entry in Add/Remove Programs (for the bundle).
The problem here is if they do an initial install with the bundle, but later only install the MSI to upgrade, they get 2 entries in the Add/Remove Programs list - the original bundle one and the new product one.
Approach #2 - Set Bundle attributes DisableRemove and DisableModify both to "yes" in the bundle. This gives a single Add/Remove Programs entry - this time for the product MSI, not the bundle.
The problem here is the following sequence:
Install bundle version 10
Uninstall ARP entry (product MSI)
Install bundle version 9
That third step fails because the system sees a version 10 bundle still installed.
Our bundle is 30MB and our product is 3MB. I don't want to have to have every subsequent update download another bundle when I know .NET is already present and not necessary. So what approach can I use to install the bundle originally but later update with an MSI and not run into the issues above?
EDIT: The only approach I can think of is to have a custom action on uninstall which detects whether or not the MSI was dispatched via the bundle (as opposed to standalone) and if it is not, it then uses something like MsiEnumRelatedProducts() to find the product guid for the bundle and inline uninstalls that. Seems super messy and I'm not sure if it would even work or if that invocation would hit some kind of "installer already running" error.
Setting ARPSYSTEMCOMPONENT = 1 in Property Table of MSI installer(or from command line) will prevent it from displaying in Add or Remove programs. So, Your first approach should work after this change.
I think the simplest solution here is to never distribute MSIs and create two versions of the bundle - one that only contains our package, and the other which contains our package plus the .NET runtime. Always mark the embedded MSI as visible=no and have the bundle's entry show up in ARP. This ensures bundles always update bundles, ARP shows the correct version, and that nothing ever gets left behind on an uninstall.
I tried to implement a custom action on MSI uninstall to query the bundle's Upgrade Code and manually remove it. Unfortunately, WiX bundles do not show up to the system as normal products. So while I could manually parse the registry, the more robust APIs like MsiEnumRelatedProducts() cannot be used. Therefore, I think the bundle-of-one is my best solution at this time.

Uninstall is not working for MSI application - Error 1722

I have created MSI using WIX 3.11. I have made some customization to the Installer to install the application in a custom folder. I am also writing Install path to registry value under HKLM\Software.
I am reading registry value in batch file for one of my use-case.
Installation is successful and the application is running fine.
But while uninstalling, I am facing below issue -
Uninstall window pops up with the message - The following applications should be closed before continuing the Install: [MyApplication]
In uninstall log while removing the service, I see below error - Error 1722. There is a problem with this Windows Installer package. A program run as part of the setup did not finish as expected. Contact your support personnel or package vendor.
If I remove the service manually, no errors can be seen and the service is getting deleted. Not sure why Uninstall is failing.
Please shed some light on this.
Stop Service Before Uninstall: In the compiled MSI, what entries do you have in the ServiceControl table? You need to stop the service before its executable is deleted. See WiX service installation sample linked to below.
Failing Custom Action: It is also possible that you have a custom action which tries to run a batch file that has already been uninstalled when you try to run it. This can be a custom action that should not run on uninstall (conditioning is wrong), or you have sequenced it incorrectly so the batch file is gone from disk - courtesy of the uninstall - before the custom action can run successfully. You need to move the custom action earlier in the installation / un-installation sequence or condition it better so it never runs on uninstall. Both issues are very common. Be aware that it is common to fail to condition custom actions so they run unexpectedly. Very often they run during major upgrade uninstalls undesirably.
Batch Files CAs: For what it is worth - and no offense: using batch files in custom action is an MSI anti-pattern in my opinion. There is basically zero error handling and hence no management and recovery from error conditions. And generally no MSI rollback support. C++ custom actions are best in my view (minimal dependencies, good debugability, full featured language, large down-to-the-metal API). Just so it is mentioned. It all depends how large your distribution is. For in-house appliations one can get away with more than for truly global package distribution. This has to do with the complexity of deployment (see section a bit down the page). There are so many error sources.
WiX Service Installation: Maybe see this hands on WiX-markup sample from Rainer Stropek: WiXSamples - github.com/rstropek. Please check
the ServiceControl element.
Common MSI Problems: I hate to "pitch" this content. It is essentially the things you can't easily find in books - and for good reason. Some rules of thumb and opinions in a chaotic form, but here it is if you want to check it out: How do I avoid common design flaws in my WiX / MSI deployment solution? Just honest opinions and practical advice - no claim to be "right", but it should help setup reliability. Hopefully.

Existing Uninstaller Broken During Upgrade - Patch MSI?

I wrote a custom action to help during upgrade of my product (from 1.0 to 1.1). Now I need to upgrade from 1.1 to 1.2 but the existing uninstaller is failing during upgrade. I got the execution conditions of my custom action wrong. (Lesson learned, always test upgrading to the next version before deploying).
Right now it seems my best option is to modify the InstallExecuteSequence table in the existing .msi to disable the failing custom actions. I'll have to create another custom action to browse the registry, locate the existing .msi in C:\
Windows\Installer, patch it, and then continue with the upgrade. This sounds like a terrible, error prone solution, but I'm really at a loss. This was supposed to be an automatic, silent upgrade pushed down from a remote cloud.
Another option would be to write a batch script to uninstall the existing product, then execute the new installer.
Any advice?
EDIT This question is already answered here: I screwed up, how can I uninstall my program?
The supported way to do this is a patch (by which I mean an MSP file, not coding to alter the cached MSI file). That's by far the most straightforward way to get out of the situation. After that, do the upgrade. Using WiX you could probably put the MSP and the upgrade in a bundle.
In any case, you wouldn't do your proposed change with another MSI. A small executable can do what you propose, and:
MsiGetProductInfo (ProductCode, …, INSTALLPROPERTY_LOCALPACKAGE)
is how you find the cached MSI.
Conditioning: What condition did you set on the failing custom action? And more importantly, what is the new condition you are intending to use? It sounds like regular uninstall works but major upgrade fails? The typical problem is that uninstall fails altogether, and then the usual solution is a minor upgrade which I will quickly describe.
Minor Upgrade: Normally what I use is a minor upgrade to fix whatever is wrong in the current install's (un)installation sequence(s). A minor upgrade does not uninstall the existing installation, it upgrades it "in-place", and the uninstall sequence is hence never called and thusly you avoid all its errors from manifesting themselves. There is no need to browse to the cached MSI file and hack it manually if you do things correctly in your minor upgrade. The updating of the cached MSI will happen auto-magically by the Windows Installer Engine provided you install with the correct minor upgrade command line.
Future Upgrades: A minor upgrade will generally always work if you make it simple enough, but the problem is usually applying it since it often targets only a single, previous version. When you get to the next release and if you then use a major upgrade, you will see the error in your original MSI manifest itself on uninstall if you are upgrading an installation that never had the minor upgrade applied - in other words it is still the oldest version of your installation. This is generally solved by a setup.exe launcher which will install the minor upgrade if need be. The bad news is that you need to keep that update in every future release - if you want to avoid any upgrade errors. Or in a corporate environment you would use the distribution system to check what is already on the box and install accordingly. If your manual uninstall works correctly (but major upgrade uninstall fails), all you should need to do is to push an uninstall command line to msiexec.exe as the first command to run via your setup.exe I think. Then there is no need to include any minor upgrade binaries in your setup.exe launcher.
Detect & Abort?: Michael Urman's answer here explains how it might be difficult to make sure that the minor upgrade is present on the box before applying the next version of your software:
InstallShield fails because of a bad uninstall. He suggests making your package better at detecting whether a new upgrade can be safely applied.
Some Links:
how to omit a component when we try to build .msi using wix (on how patching is just a distribution mechanism for MSI upgrades that must already be working)
Is there any possible way to perform upgrade when Product codes for old and new versions are same? (on minor upgrades and their technical limitations)
Here is a hack that I got working, but based on the answers above it looks like it's not the preferred way.
[CustomAction]
public static ActionResult Patch11Installer(Session session)
{
string localPackage = NativeMethods.GetMsiInstallSource("{MY-PRODUCT-CODE}");
if (String.IsNullOrEmpty(localPackage))
{
session.Log("Failed to locate the local package");
return ActionResult.Failure;
}
session.Log($"Found local package at {localPackage}");
using (Database database = new Database(localPackage, DatabaseOpenMode.Direct))
{
foreach (string action in new string[] { LIST OF CUSTOM ACTION NAMES })
{
session.Log($"Modifying condition for action {action}");
database.Execute($"UPDATE InstallExecuteSequence SET Condition='WIX_UPGRADE_DETECTED' WHERE Action='{action}'");
}
database.Commit();
}
return ActionResult.Success;
}
The custom action calls MsiGetProductInfo to query for the v1.1 MSI using the v1.1 product code which I obtained from installer log files. It then opens the MSI database and modifies the Condition property of the InstallExecuteSequence table for the list of custom actions that are failing. It changes the Condition from "UPGRADINGPRODUCTCODE OR WIX_UPGRADE_DETECTED" to "WIX_UPGRADE_DETECTED". UPGRADINGPRODUCTCODE is the property that's causing the uninstall to fail during a major upgrade as this property is passed to the uninstaller and contains the new product code; the product code for v1.2 in my case. Here is the custom action definition in my installer file.
<CustomAction Id="Patch11Installer" Return="check" Impersonate="yes" Execute="immediate" BinaryKey="MyUpgradeCustomActions" DllEntry="Patch11Installer" />
I'll look into implementing a minor upgrade as suggested in other answers. I just thought I would leave this solution here.

Wix - During install/upgrade a Custom Action's conditions resolve to False when they shouldn't

The Setup
So I have a Custom Action in my InstallExecuteSequence that looks like this:
<Custom Action="UpdateConfigFile" After="InstallFinalize">NOT Installed OR Upgrading</Custom>
Where Upgrading is defined to:
<SetProperty After="SetFirstInstall" Id="Upgrading" Value="true">
WIX_UPGRADE_DETECTED AND NOT (REMOVE="ALL")
</SetProperty>
The Custom Action updates a web.config file with a value that is passed into the installer via a Property. It is a VB.Net function.
The Issue I'm Running Into
This Custom Action has always executed in many of our installers. But for a certain environment that we have in development, the conditions for it resolve to False on a install/upgrade scenario. It says "(condition is false)" in the MSI log.
The environment is used for development purposes such as testing the product after installed/updated.
What I'm Trying to Accomplish
I would like this to be resolved so that this environment can install our product successfully.
What I've Done So Far
I've installed the same installer to different Operating Systems such as Windows 10, 2012 R2, and 2016. The installer works just fine since the Custom Action runs as expected.
The troublesome environment is a Windows Server 2012 R2 machine. Which makes me even more confused.
I did some digging in and was only able to find this link: (https://blogs.msdn.microsoft.com/heaths/2006/07/11/why-a-custom-action-may-not-run/#comments)
From the link's suggestion, I think it is really odd if a dependency was missing because the same installer works on other machines.
So I'm pretty stumped at this point. Any help or direction would be very much appreciated. If I'm not being clear enough, feel free to ask for more clarification. Please and thank you.
Just some comments to get going, this isn't a real answer:
First things first: in order to be passed properly to deferred mode (InstallExecuteSequence) properties should be UPPERCASE (public properties) and they should be listed in the SecureCustomProperties delimited list of "safe properties" to pass to deferred mode.
Other than uppercasing it, I wouldn't set a property called Upgrading like that, I would rather use the "raw" conditions for the custom action in deferred mode (InstallExecuteSequence).
Here is a cheat sheet of common conditions from Flexera (makers of Installshield): Common MSI Conditions Cheat Sheet. And here is a direct link to the PDF.
You can have a look at this old post: How to add a WiX custom action that happens only on uninstall (via MSI)?. I have never had the time to test all those conditions, but at face value it looks correct.
Some notes about the special property UPGRADINGPRODUCTCODE.
UPGRADE:
Questions:
That Windows Server 2012 R2 machine - is there anything special about it? Is it tightened for security? What is its task or purpose on the network?
What is the actual implementation of UpdateConfigFile - is it a script, a compiled DLL written in C++, a managed DLL written in a .NET language, or something else? Maybe an EXE file?
Does any of this sound familiar: Installer fails on Windows Server 2012 R2
Did you verify that the correct version of the .NET framework is installed on this problem server? Does the function run interactively? Try running that VB.NET function interactively on that computer via a test harness EXE if it is a DLL function. Or just run it straight is it is an EXE file outright.
The issue is likely to be that you are scheduled after InstallFinalize (which is not good, see later) because the Upgrading property is not public (not uppercase). If you change the condition to WIX_UPGRADE_DETECTED AND NOT (REMOVE="ALL") it should work, assuming:
WIX_UPGRADE_DETECTED is the actual property associated with the major upgrade, such as from the major uypgrade element.
WIX_UPGRADE_DETECTED is in SecureCustomProperies.
Actions after InstallFinalize are generally not recommended because if they fail (in your case screw up the config file) then there is not much that can be done (such as roll back to a correct config file). It's effectively after the install, so it's also easier to do the config changes in the app when the upgraded version runs for the first time.
After helping the team responsible for the environment, we found out that it was a certain scenario that was causing this issue. Digging deep into the MSI log further, we found that a previous Custom Action running, also after InstallFinalize, was returning a Failure Action Result. Since that happened, the following Custom Actions such as my UpdateConfigFile Custom Action, don't execute. Resulting in the "(condition is false)" entry in the log.
Once we figured that out, I was able to reproduce it on my local environment.
Since these Custom Actions are after InstallFinalize, I thought that those Custom Actions would still run if one failed because we're past the point of the installer being able to do a Rollback. But I was wrong and now I know.
Thanks for everyone helping me out in trying to figure out this issue. I was getting worried that I was going to try to figure this out for days and start pulling my hair out. XD

Installing multiple instances of an Application with Wix Toolset

I simply need to install multiple instances of my application saving them in different folders, with no shortcut on desktop.
In other words, when the App is already installed in a Folder, if I double-click the .msi file once again, the installer shouldn’t ask me if I want repair or remove my App, but it simply should permit to install it in a new folder.
How can I solve this problem?
I used to work with this kind of installations before, and I would agree with #Nikolay - it is rather an exception, than the rule when it comes to Windows Installer based installations. Component rules are often tricky to follow, and multiple instances aspect adds some complexity on top. So, think twice before you go this road.
Being complex, it is still possible. Years ago I published the article of how to start authoring multiple instance installations with WiX 3.6. Note that this version of WiX simplifies it significantly. It's not a short read, so here is a quick digest:
You won't be able to achieve the "install each new instance with double-clicking MSI file" behavior. You have to have a bootstrapper - something that passes correct command line parameters to msiexec.exe.
Don't try to support unlimited number of instances - try sticking with reasonably big number. Do you imagine someone installing your app 10 times on a machine? 50? 100? Make a sane choice - this will be the number of your <Instance/> elements.
Although you only have to decorate non-file data components with MultiInstance attribute, I don't think it will break if you add it to all of your components.
Although I explained the patching of multiple instances in that post, I would only use it in production if I had no other choice.
What you are asking for is not normal in Windows. Normally, each program (product) is installed only once. I.e. each installation package has it's ID (called "ProductID"). If that ID already registered in the system as installed, the system will not allow you to install the second product with the same ProductID, but start change/remove.
What you can do:
Don't use Windows Installer (and WIX), use ZIP for example, or some self-extracting archive, or some other program which does not register installed product in the system.
Use command line to change product id before installing if you want MSI and Windows Installer for whatever reason. Try googling on "use transforms to install the same MSI multiple times". Thus you can have the same MSI per-transformed before installation, so that it looks as a different one to the system.
Install per-user, if that's good enough for you (i.e. don't install to Program Files, install to user folder)
Maybe there are other options...