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; }
/// <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); }