-
Notifications
You must be signed in to change notification settings - Fork 1
DetectPhase Class
Bootstrapper.Phases.DetectPhase
For this tutorial, we'll use the detect phase to determine if the bundle has been previously installed, and if so, what version is installed. WiX will use the bundle's upgrade and product codes for this.
In the OnDetectBegin
method, we determine if the currently running bundle is registered or not. We'll save this to a field for later use.
if (e.RegistrationType == RegistrationType.Full)
_bundleDetectedState = DetectionState.Present;
else
_bundleDetectedState = DetectionState.Absent;
The DetectRelatedBundle
event is triggered when WiX finds an installed bundle that is related to the currently running bundle, but isn't the currently running bundle. For this installer, we can keep things simple. We only care if Windows Installer detected a major upgrade relationship. That means the upgrade code matches, but the product code and versions differ.
If an upgrade is detected, we'll save off the version of the installed bundle and determine if it's newer or older.
if (e.RelationType == RelationType.Upgrade)
{
_bundleDetectedState = DetectionState.Present;
if (string.IsNullOrWhiteSpace(_model.State.RelatedBundleVersion))
_model.State.RelatedBundleVersion = e.Version;
if (_model.Engine.CompareVersions(_model.State.BundleVersion, e.Version) > 0)
{
if (_model.State.RelatedBundleStatus <= BundleStatus.Current)
_model.State.RelatedBundleStatus = BundleStatus.OlderInstalled;
}
else if (_model.Engine.CompareVersions(_model.State.BundleVersion, e.Version) == 0)
{
if (_model.State.RelatedBundleStatus == BundleStatus.NotInstalled)
_model.State.RelatedBundleStatus = BundleStatus.Current;
}
else
_model.State.RelatedBundleStatus = BundleStatus.NewerInstalled;
}
After determining what type of upgrade was detected, we can add the detected bundle to the chain of packages in the running bundle. This detected bundle will be scheduled for uninstall.
if (!_model.State.Bundle.Packages.ContainsKey(e.ProductCode))
_model.State.Bundle.AddRelatedBundleAsPackage(e);
This is where we can finalize the bundle's status on the machine. Before we do that, we should look at the final HRESULT of the detect phase to make sure everything went as expected.
_model.State.PhaseResult = e.Status;
If the detect phase failed for any reason, we'll log the results and, if the bundle is running silently, call the UI facade's ShutDown
method. This stops the Windows message loop and allows the BA to exit.
if (ErrorHelper.HResultIsFailure(e.Status))
{
...
if (!_model.UiFacade.IsUiShown)
_model.UiFacade.ShutDown();
return;
}
With that out of the way, next check to see if a related bundle was detected. If it wasn't, then the app state's RelatedBundleStatus
property will be unknown, so let's sort out whether the currently running bundle is installed or not based on the detected state we saved in OnDetectBegin
.
if (_model.State.RelatedBundleStatus == BundleStatus.Unknown)
{
if (_bundleDetectedState == DetectionState.Present)
{
_model.State.RelatedBundleStatus = BundleStatus.Current;
if (string.IsNullOrWhiteSpace(_model.State.RelatedBundleVersion))
_model.State.RelatedBundleVersion = _model.State.BundleVersion;
}
else
_model.State.RelatedBundleStatus = BundleStatus.NotInstalled;
}
At this point, we know if the bundle is installed and which version. Set the BA status to waiting. The UI will know it's safe to ask the user for info when the BA is waiting.
_model.State.BaStatus = BaStatus.Waiting;
Tip
This tutorial doesn't cover how to handle user configurable options very well, like install folder. But if your UI asks the user for info before allowing them to install or update and your installer supports a silent mode with command line parameters, then this tip is for you. Now would be a good time to verify all command line options before allowing the install to proceed. If validation fails, you should set the app state's PhaseResult
property to an appropriate HRESULT failure code. This is converted to a win32 error and returned when the BA exits. The easiest way to handle this is to use the HRESULT from a .NET exception. Native exceptions all have HRESULTS which convert nicely to win32 errors.
_model.State.PhaseResult = new InvalidOperationException().HResult;
Now to figure out how to proceed with the install. If it should be automated, then start the plan phase, otherwise, clean up and let the UI take over. In the sample solution, I perform a check to see if the user selected the Uninstall option from Add/Remove Programs. If they did, then I start an automatic uninstall.
if (_model.CommandInfo.Action == LaunchAction.Uninstall && _model.CommandInfo.Resume == ResumeType.Arp)
{
_model.Log.Write("Starting plan for automatic uninstall");
followupAction = _model.CommandInfo.Action;
}
If the user selected the -quiet
or -passive
command line switch, or the bundle is running embedded, let's start the plan phase.
if (_model.State.Display != Display.Full)
{
_model.Log.Write("Starting plan for silent mode.");
followupAction = _model.CommandInfo.Action;
}
Before starting the plan phase, notify the UI that detect has completed. Let it know what action is planned as well.
NotifyDetectComplete(followupAction);
Finally, if an action has been selected for planning, start the plan phase. Otherwise, the detect phase is done and the UI can take things from here.
if (followupAction != LaunchAction.Unknown)
_model.PlanAndApply(followupAction);