public void Execute()
        {
            List <PackageFacade> orderedFacades = new List <PackageFacade>();
            List <WixBundleRollbackBoundaryRow> usedBoundaries = new List <WixBundleRollbackBoundaryRow>();

            // Process the chain of packages to add them in the correct order
            // and assign the forward rollback boundaries as appropriate. Remember
            // rollback boundaries are authored as elements in the chain which
            // we re-interpret here to add them as attributes on the next available
            // package in the chain. Essentially we mark some packages as being
            // the start of a rollback boundary when installing and repairing.
            // We handle uninstall (aka: backwards) rollback boundaries after
            // we get these install/repair (aka: forward) rollback boundaries
            // defined.
            WixBundleRollbackBoundaryRow previousRollbackBoundary = null;
            WixBundleRollbackBoundaryRow lastRollbackBoundary     = null;
            bool boundaryHadX86Package = false;

            foreach (WixGroupRow row in this.WixGroupTable.Rows)
            {
                if (ComplexReferenceChildType.Package == row.ChildType && ComplexReferenceParentType.PackageGroup == row.ParentType && "WixChain" == row.ParentId)
                {
                    PackageFacade facade = null;
                    if (PackageFacades.TryGetValue(row.ChildId, out facade))
                    {
                        if (null != previousRollbackBoundary)
                        {
                            usedBoundaries.Add(previousRollbackBoundary);
                            facade.Package.RollbackBoundary = previousRollbackBoundary.ChainPackageId;
                            previousRollbackBoundary        = null;

                            boundaryHadX86Package = (facade.Package.x64 == YesNoType.Yes);
                        }

                        // Error if MSI transaction has x86 package preceding x64 packages
                        if ((lastRollbackBoundary != null) && (lastRollbackBoundary.Transaction == YesNoType.Yes) &&
                            boundaryHadX86Package &&
                            (facade.Package.x64 == YesNoType.Yes))
                        {
                            Messaging.Instance.OnMessage(WixErrors.MsiTransactionX86BeforeX64(lastRollbackBoundary.SourceLineNumbers));
                        }
                        boundaryHadX86Package = boundaryHadX86Package || (facade.Package.x64 == YesNoType.No);

                        orderedFacades.Add(facade);
                    }
                    else // must be a rollback boundary.
                    {
                        // Discard the next rollback boundary if we have a previously defined boundary.
                        WixBundleRollbackBoundaryRow nextRollbackBoundary = Boundaries.Get(row.ChildId);
                        if (null != previousRollbackBoundary)
                        {
                            Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.ChainPackageId));
                        }
                        else
                        {
                            previousRollbackBoundary = nextRollbackBoundary;
                            lastRollbackBoundary     = nextRollbackBoundary;
                        }
                    }
                }
            }

            if (null != previousRollbackBoundary)
            {
                Messaging.Instance.OnMessage(WixWarnings.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.ChainPackageId));
            }

            // With the forward rollback boundaries assigned, we can now go
            // through the packages with rollback boundaries and assign backward
            // rollback boundaries. Backward rollback boundaries are used when
            // the chain is going "backwards" which (AFAIK) only happens during
            // uninstall.
            //
            // Consider the scenario with three packages: A, B and C. Packages A
            // and C are marked as rollback boundary packages and package B is
            // not. The naive implementation would execute the chain like this
            // (numbers indicate where rollback boundaries would end up):
            //      install:    1 A B 2 C
            //      uninstall:  2 C B 1 A
            //
            // The uninstall chain is wrong, A and B should be grouped together
            // not C and B. The fix is to label packages with a "backwards"
            // rollback boundary used during uninstall. The backwards rollback
            // boundaries are assigned to the package *before* the next rollback
            // boundary. Using our example from above again, I'll mark the
            // backwards rollback boundaries prime (aka: with ').
            //      install:    1 A B 1' 2 C 2'
            //      uninstall:  2' C 2 1' B A 1
            //
            // If the marked boundaries are ignored during install you get the
            // same thing as above (good) and if the non-marked boundaries are
            // ignored during uninstall then A and B are correctly grouped.
            // Here's what it looks like without all the markers:
            //      install:    1 A B 2 C
            //      uninstall:  2 C 1 B A
            // Woot!
            string        previousRollbackBoundaryId = null;
            PackageFacade previousFacade             = null;

            foreach (PackageFacade package in orderedFacades)
            {
                if (null != package.Package.RollbackBoundary)
                {
                    if (null != previousFacade)
                    {
                        previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId;
                    }

                    previousRollbackBoundaryId = package.Package.RollbackBoundary;
                }

                previousFacade = package;
            }

            if (!String.IsNullOrEmpty(previousRollbackBoundaryId) && null != previousFacade)
            {
                previousFacade.Package.RollbackBoundaryBackward = previousRollbackBoundaryId;
            }

            this.OrderedPackageFacades  = orderedFacades;
            this.UsedRollbackBoundaries = usedBoundaries;
        }
Example #2
0
        /// <summary>
        /// Creates a new row in the table.
        /// </summary>
        /// <param name="sourceLineNumbers">Original source lines for this row.</param>
        /// <param name="add">Specifies whether to only create the row or add it to the table automatically.</param>
        /// <returns>Row created in table.</returns>
        public Row CreateRow(SourceLineNumber sourceLineNumbers, bool add = true)
        {
            Row row;

            switch (this.Name)
            {
            case "BBControl":
                row = new BBControlRow(sourceLineNumbers, this);
                break;

            case "WixBundlePackage":
                row = new WixBundlePackageRow(sourceLineNumbers, this);
                break;

            case "WixBundleExePackage":
                row = new WixBundleExePackageRow(sourceLineNumbers, this);
                break;

            case "WixBundleMsiPackage":
                row = new WixBundleMsiPackageRow(sourceLineNumbers, this);
                break;

            case "WixBundleMspPackage":
                row = new WixBundleMspPackageRow(sourceLineNumbers, this);
                break;

            case "WixBundleMsuPackage":
                row = new WixBundleMsuPackageRow(sourceLineNumbers, this);
                break;

            case "Component":
                row = new ComponentRow(sourceLineNumbers, this);
                break;

            case "WixBundleContainer":
                row = new WixBundleContainerRow(sourceLineNumbers, this);
                break;

            case "Control":
                row = new ControlRow(sourceLineNumbers, this);
                break;

            case "File":
                row = new FileRow(sourceLineNumbers, this);
                break;

            case "WixBundleMsiFeature":
                row = new WixBundleMsiFeatureRow(sourceLineNumbers, this);
                break;

            case "WixBundleMsiProperty":
                row = new WixBundleMsiPropertyRow(sourceLineNumbers, this);
                break;

            case "Media":
                row = new MediaRow(sourceLineNumbers, this);
                break;

            case "WixBundlePayload":
                row = new WixBundlePayloadRow(sourceLineNumbers, this);
                break;

            case "Property":
                row = new PropertyRow(sourceLineNumbers, this);
                break;

            case "WixRelatedBundle":
                row = new WixRelatedBundleRow(sourceLineNumbers, this);
                break;

            case "WixBundleRelatedPackage":
                row = new WixBundleRelatedPackageRow(sourceLineNumbers, this);
                break;

            case "WixBundleRollbackBoundary":
                row = new WixBundleRollbackBoundaryRow(sourceLineNumbers, this);
                break;

            case "Upgrade":
                row = new UpgradeRow(sourceLineNumbers, this);
                break;

            case "WixBundleVariable":
                row = new WixBundleVariableRow(sourceLineNumbers, this);
                break;

            case "WixAction":
                row = new WixActionRow(sourceLineNumbers, this);
                break;

            case "WixApprovedExeForElevation":
                row = new WixApprovedExeForElevationRow(sourceLineNumbers, this);
                break;

            case "WixBundle":
                row = new WixBundleRow(sourceLineNumbers, this);
                break;

            case "WixBundlePackageExitCode":
                row = new WixBundlePackageExitCodeRow(sourceLineNumbers, this);
                break;

            case "WixBundlePatchTargetCode":
                row = new WixBundlePatchTargetCodeRow(sourceLineNumbers, this);
                break;

            case "WixBundleSlipstreamMsp":
                row = new WixBundleSlipstreamMspRow(sourceLineNumbers, this);
                break;

            case "WixBundleUpdate":
                row = new WixBundleUpdateRow(sourceLineNumbers, this);
                break;

            case "WixBundleCatalog":
                row = new WixBundleCatalogRow(sourceLineNumbers, this);
                break;

            case "WixChain":
                row = new WixChainRow(sourceLineNumbers, this);
                break;

            case "WixChainItem":
                row = new WixChainItemRow(sourceLineNumbers, this);
                break;

            case "WixBundlePackageCommandLine":
                row = new WixBundlePackageCommandLineRow(sourceLineNumbers, this);
                break;

            case "WixComplexReference":
                row = new WixComplexReferenceRow(sourceLineNumbers, this);
                break;

            case "WixDeltaPatchFile":
                row = new WixDeltaPatchFileRow(sourceLineNumbers, this);
                break;

            case "WixDeltaPatchSymbolPaths":
                row = new WixDeltaPatchSymbolPathsRow(sourceLineNumbers, this);
                break;

            case "WixFile":
                row = new WixFileRow(sourceLineNumbers, this);
                break;

            case "WixGroup":
                row = new WixGroupRow(sourceLineNumbers, this);
                break;

            case "WixMedia":
                row = new WixMediaRow(sourceLineNumbers, this);
                break;

            case "WixMediaTemplate":
                row = new WixMediaTemplateRow(sourceLineNumbers, this);
                break;

            case "WixMerge":
                row = new WixMergeRow(sourceLineNumbers, this);
                break;

            case "WixPayloadProperties":
                row = new WixPayloadPropertiesRow(sourceLineNumbers, this);
                break;

            case "WixProperty":
                row = new WixPropertyRow(sourceLineNumbers, this);
                break;

            case "WixSimpleReference":
                row = new WixSimpleReferenceRow(sourceLineNumbers, this);
                break;

            case "WixUpdateRegistration":
                row = new WixUpdateRegistrationRow(sourceLineNumbers, this);
                break;

            case "WixVariable":
                row = new WixVariableRow(sourceLineNumbers, this);
                break;

            default:
                row = new Row(sourceLineNumbers, this);
                break;
            }

            if (add)
            {
                this.Rows.Add(row);
            }

            return(row);
        }