WiX Bootstrapper: Rollback notification - wix

In the bundle.wxs of my managed bootstrapper, I have chained multiple packages:
<Chain>
<ExePackage Id="Test1"......>
<ExePackage Id="Test2"......>
<ExePackage Id="MicrosoftVCPP2005Redistributable" SourceFile="..\Tools\VC2005Redistributable\vcredist_x86.exe" Vital="yes" InstallCondition="SelectedDBSize1 = 24" />
</Chain>
I'm subscribing to the ExecutePackageBegin/ExecutePackageComplete events to check which package is currently being executed and accordingly display the progress text indicating which installation is in progress.
But, if due to some reason a roll back action starts midway, I want to change the progress text to indicate that rollback is in progress. Is there any event available when there is a switch from installation to rollback? Or do I have to check the sequence of the packages being invoked and decide based on that?

The documentation for the property is a little bit wrong but the ExecutePackageBeginEventArgs class's ShouldExecute property will tell you if the package is being "executed" or "rolled back". In your case, when the ExecutePackageBeginEventArgs.ShouldExecute=false then you know that package is being rolled back.

Related

Wix scheduling custom actions

In the process of converting from installshield to wix, I am porting over the custom actions.
One is for determining previously installed versions and popping up a message if any are found. I guess the guids weren't properly tracked before.
Within installshield, it appears they had the CA scheduled after ValidateProductID. I tried to do the same:
<CustomAction Id="CA_CheckPreviousVersions.SetProperty" Property="CA_CheckPreviousVersions" Value="ERROR_UNINSTALL_VERSION=$(var.ERROR_UNINSTALL_VERSION)" />
<CustomAction Id="CA_CheckPreviousVersions" DllEntry="CheckPreviousVersions" Execute="deferred" BinaryKey="LunaClientCustomActions_dll" />
<InstallExecuteSequence>
...
<Custom Action="CA_CheckPreviousVersions.SetProperty" After="ValidateProductID" />
<Custom Action="CA_CheckPreviousVersions" After="CA_CheckPreviousVersions.SetProperty" >NOT Installed AND NOT PATCH</Custom>
</InstallExecuteSequence>
Except, I get a nasty warning:
error LGHT0204: ICE77: CA_CheckPreviousVersions is a in-script custom action. It must be sequenced in between the InstallInitialize action and the InstallFinalize action in the InstallExecuteSequence table
Why am I getting this, whereas IS seemingly permitted it? More to the point, how do I replicate the behaviour that was previously in place?
You haven't shown the custom action definition, but the message indicates it's deferred and must be sequenced as the message indicates. Perhaps the original type was immediate.
If you mean previously installed versions of your product (or in fact any MSI-based product for which you know the UpgradeCode) there is no need for any code. If you were to use a WiX MajorUpgrade element you can detect previous versions because the WIX_UPGRADE_DETECTED property will be set, and you could use that as a launch condition, or whatever it is you plan to do. Or use Upgrade and UpgradeVersion elements to detect previous versions and version ranges. From what you've said there seems no need to run code. Just use the upgrade search properties and show dialogs or errors or whatever.

Pass MSI Property before Install Finalize

I have a variable in my burn bundle as such:
<Variable Name="DemoVariable" Type="string" Value="ChangedProperty" bal:Overridable="yes"/>
Which is then used with an MSIProperty:
<MsiPackage SourceFile="testFile.msi" Id="DemoPackageId_1" Cache="yes" Visible="no">
<MsiProperty Name="PassedProperty" Value="[DemoVariable]"/>
</MsiPackage>
In my testFile.msi I have a property and custom action:
<Property Id="PassedProperty" Value="Unchanged"/>
<Binary Id="CustomActionDll"
SourceFile="CustomAction.CA.dll"/>
<InstallExecuteSequence>
<Custom Action="ShowMessageBoxCA" Before="CostFinalize"/>
</InstallExecuteSequence>
<CustomAction Id="ShowMessageBoxCA"
Return="check"
Execute="firstSequence"
BinaryKey="CustomActionDll"
DllEntry="ShowMessageBox"
HideTarget="no" />
The ShowMessageBox function literally just calls:
MessageBox.Show(session["PassedProperty"])
The problem is that the message box shows the message "unchanged". I looked through the log file for the installation and I can see that the PassedProperty is being changed sometime after InstallFinalize finishes. This is obviously too late for my custom action which takes place before CostFinalize.
Is there a way to make Burn change the MSIProperty earlier in the process rather than making the custom actions take place later?
Edit:
Okay so I found the answer but I can't post it yet so I'll leave the information here in an edit.
Basically MSI's have Public and Private properties. Only public properties are declared before the install phase takes place. Public properties must be all uppercase so to fix my issue I just replaced all instance of "Passed_Property" with "PASSED_PROPERTY" and it works fine.
So the problem I had was that I was creating my properties as private properties, which aren't available until after the installation. To make your properties public and therefore available throughout the whole install, they need to be Uppercase.
<Property Id="PASSED_PROPERTY" Value="Unchanged"/>
https://msdn.microsoft.com/en-us/library/aa370912(v=vs.85).aspx
As soon as I made them public the properties were changed by Burn.

Retain desktop shortcut position on major upgrade in WiX

My WiX installer optionally creates a desktop shortcut upon installation and removes it when it is uninstalled, a CREATEDESKTOPSHORTCUT property handles that, so nothing special so far.
When a major upgrade is performed, the shortcut is removed and recreated, resetting its position on the desktop to the first free spot. This is the default major upgrade behaviour, but how can I retain its position?
An obvious first approach involves using the WIX_UPGRADE_DETECTED property which is only present if a major upgrade is running (I only use major upgrades). The desired behaviour is
Create a shortcut on the initial installation if the product is not yet installed and CREATEDESKTOPSHORTCUT is true
Don't touch the shortcut on a major upgrade (e.g. if WIX_UPGRADE_DETECTED is present), regardless if it had been created on the initial installation or not
Remove the shortcut when the product is uninstalled and if it had been created on the initial installation
Is there a way to achieve that? I guess it might boil down to a Condition element inside the shortcut's Component or Feature, but I don't get it up and running.
Edit:
One possible approach is to make the shortcut creating component permanent and introduce an additional component that is only triggered when the product is removed (and not major upgraded) and explicitly removes the link file from the desktop:
<DirectoryRef Id="DesktopFolder">
<Component Id="CreateDesktopLink" Guid="My-GUID-1" Permanent="yes">
<RegistryValue ... />
<Shortcut Id="CmdShortcut" Name="My Desktop Link" Target="[SystemFolder]cmd.exe" />
</Component>
<Component Id="RemoveDesktopLink" Guid="My-GUID-2">
<RegistryValue ... />
<RemoveFile Id="RemoveDesktopLinkFile" Name="My Desktop Link.lnk" On="uninstall" />
<Condition>REMOVE=ALL AND NOT WIX_UPGRADE_DETECTED</Condition>
</Component>
</DirectoryRef>
although that would leave an orphaned component behind on uninstall. Optionally, I could move the shortcut creation into a custom action, eliminating the permanent component.
Any input is appreciated.
You can't preserve the shortcut - at least not in the default case of advertised shortcuts. These shortcuts contain a reference to the product, component and feature, and sometimes an icon that may be in the binary table of the cached MSI. At least two of these will change over an upgrade.
That leaves you with non-advertised shortcuts or ones you create yourself, for which attempting to preserve the location might work.
Perhaps you could enumerate the desktop icons (a search has code examples) so you can put the icon back where it was before, because that would work for all flavors of shortcut and you wouldn't need to change your upgrade logic. Just find out where it currently is, then find it again after it's installed and move it the original location.
How about sequencing your "RemoveExistingProducts" standard action after InstallFinalize?
Doing so would mean that the newer product is installed followed by an uninstall of the older product. When the newer product is installed, the reference count of the component creating the shortcut will get incremented to 2 and hence when the older product is uninstalled, the component creating the shortcut would be untouched.
I am just thinking that this is one other possible solution.
However, re-sequencing "RemoveExistingProducts" is something which you will have to think through deeply.

WiX: returning a value from exepackage

I have been asked to add a function to an existing WiX package.
Specifically, I need to run a small c# application and return a single int back to WiX to conditionally control further actions.
I can see from ExePackage help that there is an ExitCode, but this is an enumeration of success, error, scheduleReboot or forceReboot.
I have googled quite a bit and I am wondering if I am missing the point. I can probably implement the C# process internally within WiX to get the user to provide the information I need, but the existing package already has custom ExePackages written in C# with a particular style, so I'd like to stay with that if I can. (The existing packages don't return any needed values)
Can I do this, or do I need to try and operate entirely within WiX?
For reference, one of the existing packages looks like this:
<ExePackage
SourceFile="..."
DisplayName="License Key"
InstallSize="0"
Permanent="yes"
InstallCommand="/ignoreIfLicensed"
RepairCommand="/ignore"
UninstallCommand="/ignore"
/>
The reference to <ExePackage ...> implies you want the condition to operate in a WIX bundle. In this scenario I think your options are limited and you can only map the return value of an ExePackage to global behaviour like forceReboot.
Do you have any <MsiPackage...> references? If you have, you could move the conditional behaviour inside each <MsiPackage...> using a Custom Action to call the exe and set a property. The property can then be used as a condition in each <component...> you want to conditionally install. See Using a WiX custom action to set a property's value for more information on custom actions setting properties.

custom action after installation failed

How to sequence a custom action to execute only when installation is interrupted or completed with error?
Is there any windows installer property which returns current installation status (failed/succeed)?
The OnExit attribute of the <Custom> element is what you're looking for. It is mutually exclusive with Before, After, and Sequence attributes and can have the following values: success, cancel, error, suspend.
UPDATE: basically, this is what I mean:
1) Define a custom action which will do the work you'd like it to do (gather some failure data). Note that you'll have to define N custom actions pointing to the same target because the CustomAction MSI table expects Id as a primary key (let's assume it's DLL CA):
<CustomAction Id="LogFailureOnCancel" BinaryKey="CustomActions" DllEntry="LogFailure" ... />
<CustomAction Id="LogFailureOnError" BinaryKey="CustomActions" DllEntry="LogFailure" ... />
NOTE: both definitions point to the same real action (DllEntry attribute).
2) Schedule these custom actions appropriately:
<Custom Action="LogFailureOnCancel" OnExit="cancel" />
<Custom Action="LogFailureOnError" OnExit="error" />
To the extent that Windows Installer allows it, this is only possible during custom actions scheduled for rollback. However rollback only applies to the deferred script, so it's only available for actions scheduled between InstallInitialize and InstallFinalize. If an immediate-mode action outside this scheduling window causes the installation to abort, rollback does not apply.
Assuming you're using a C++ or InstallScript action, you can find out if rollback is enabled, and if you're in rollback, with calls to MsiGetMode - using MSIRUNMODE_ROLLBACKENABLED or MSIRUNMODE_ROLLBACK respectively.