/// <summary> /// Binds a bundle. /// </summary> /// <param name="bundle">The bundle to bind.</param> /// <param name="bundleFile">The bundle to create.</param> /// <returns>true if binding completed successfully; false otherwise</returns> private bool BindBundle(Output bundle, string bundleFile) { // First look for data we expect to find... Chain, WixGroups, etc. Table chainPackageTable = bundle.Tables["ChainPackage"]; if (null == chainPackageTable || 0 == chainPackageTable.Rows.Count) { // We shouldn't really get past the linker phase if there are // no group items... that means that there's no UX, no Chain, // *and* no Containers! throw new WixException(WixErrors.MissingBundleInformation("ChainPackage")); } Table wixGroupTable = bundle.Tables["WixGroup"]; if (null == wixGroupTable || 0 == wixGroupTable.Rows.Count) { // We shouldn't really get past the linker phase if there are // no group items... that means that there's no UX, no Chain, // *and* no Containers! throw new WixException(WixErrors.MissingBundleInformation("WixGroup")); } // Ensure there is one and only one row in the WixBundle table. // The compiler and linker behavior should have colluded to get // this behavior. Table bundleTable = bundle.Tables["WixBundle"]; if (null == bundleTable || 1 != bundleTable.Rows.Count) { throw new WixException(WixErrors.MissingBundleInformation("WixBundle")); } // Ensure there is one and only one row in the WixBootstrapperApplication table. // The compiler and linker behavior should have colluded to get // this behavior. Table baTable = bundle.Tables["WixBootstrapperApplication"]; if (null == baTable || 1 != baTable.Rows.Count) { throw new WixException(WixErrors.MissingBundleInformation("WixBootstrapperApplication")); } // Ensure there is one and only one row in the WixChain table. // The compiler and linker behavior should have colluded to get // this behavior. Table chainTable = bundle.Tables["WixChain"]; if (null == chainTable || 1 != chainTable.Rows.Count) { throw new WixException(WixErrors.MissingBundleInformation("WixChain")); } foreach (BinderExtension extension in this.extensions) { extension.BundleInitialize(bundle); } if (this.core.EncounteredError) { return false; } this.WriteBuildInfoTable(bundle, bundleFile); // gather all the wix variables Table wixVariableTable = bundle.Tables["WixVariable"]; if (null != wixVariableTable) { foreach (WixVariableRow wixVariableRow in wixVariableTable.Rows) { this.WixVariableResolver.AddVariable(wixVariableRow); } } Hashtable cabinets = new Hashtable(); ArrayList delayedFields = new ArrayList(); // localize fields, resolve wix variables, and resolve file paths this.ResolveFields(bundle.Tables, cabinets, delayedFields); // if there are any fields to resolve later, create the cache to populate during bind IDictionary<string, string> variableCache = null; if (0 < delayedFields.Count) { variableCache = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase); } if (this.core.EncounteredError) { return false; } Table relatedBundleTable = bundle.Tables["RelatedBundle"]; List<RelatedBundleInfo> allRelatedBundles = new List<RelatedBundleInfo>(); if (null != relatedBundleTable && 0 < relatedBundleTable.Rows.Count) { Dictionary<string, bool> deduplicatedRelatedBundles = new Dictionary<string, bool>(); foreach (Row row in relatedBundleTable.Rows) { string id = (string)row[0]; if (!deduplicatedRelatedBundles.ContainsKey(id)) { deduplicatedRelatedBundles[id] = true; allRelatedBundles.Add(new RelatedBundleInfo(row)); } } } // Ensure that the bundle has our well-known persisted values. Table variableTable = bundle.EnsureTable(this.core.TableDefinitions["Variable"]); VariableRow bundleNameWellKnownVariable = (VariableRow)variableTable.CreateRow(null); bundleNameWellKnownVariable.Id = Binder.BURN_BUNDLE_NAME; bundleNameWellKnownVariable.Hidden = false; bundleNameWellKnownVariable.Persisted = true; VariableRow bundleOriginalSourceWellKnownVariable = (VariableRow)variableTable.CreateRow(null); bundleOriginalSourceWellKnownVariable.Id = Binder.BURN_BUNDLE_ORIGINAL_SOURCE; bundleOriginalSourceWellKnownVariable.Hidden = false; bundleOriginalSourceWellKnownVariable.Persisted = true; VariableRow bundleOriginalSourceFolderWellKnownVariable = (VariableRow)variableTable.CreateRow(null); bundleOriginalSourceFolderWellKnownVariable.Id = Binder.BURN_BUNDLE_ORIGINAL_SOURCE_FOLDER; bundleOriginalSourceFolderWellKnownVariable.Hidden = false; bundleOriginalSourceFolderWellKnownVariable.Persisted = true; VariableRow bundleLastUsedSourceWellKnownVariable = (VariableRow)variableTable.CreateRow(null); bundleLastUsedSourceWellKnownVariable.Id = Binder.BURN_BUNDLE_LAST_USED_SOURCE; bundleLastUsedSourceWellKnownVariable.Hidden = false; bundleLastUsedSourceWellKnownVariable.Persisted = true; // To make lookups easier, we load the variable table bottom-up, so // that we can index by ID. List<VariableInfo> allVariables = new List<VariableInfo>(variableTable.Rows.Count); foreach (VariableRow variableRow in variableTable.Rows) { allVariables.Add(new VariableInfo(variableRow)); } // TODO: Although the WixSearch tables are defined in the Util extension, // the Bundle Binder has to know all about them. We hope to revisit all // of this in the 4.0 timeframe. Dictionary<string, WixSearchInfo> allSearches = new Dictionary<string, WixSearchInfo>(); Table wixFileSearchTable = bundle.Tables["WixFileSearch"]; if (null != wixFileSearchTable && 0 < wixFileSearchTable.Rows.Count) { foreach (Row row in wixFileSearchTable.Rows) { WixFileSearchInfo fileSearchInfo = new WixFileSearchInfo(row); allSearches.Add(fileSearchInfo.Id, fileSearchInfo); } } Table wixRegistrySearchTable = bundle.Tables["WixRegistrySearch"]; if (null != wixRegistrySearchTable && 0 < wixRegistrySearchTable.Rows.Count) { foreach (Row row in wixRegistrySearchTable.Rows) { WixRegistrySearchInfo registrySearchInfo = new WixRegistrySearchInfo(row); allSearches.Add(registrySearchInfo.Id, registrySearchInfo); } } Table wixComponentSearchTable = bundle.Tables["WixComponentSearch"]; if (null != wixComponentSearchTable && 0 < wixComponentSearchTable.Rows.Count) { foreach (Row row in wixComponentSearchTable.Rows) { WixComponentSearchInfo componentSearchInfo = new WixComponentSearchInfo(row); allSearches.Add(componentSearchInfo.Id, componentSearchInfo); } } Table wixProductSearchTable = bundle.Tables["WixProductSearch"]; if (null != wixProductSearchTable && 0 < wixProductSearchTable.Rows.Count) { foreach (Row row in wixProductSearchTable.Rows) { WixProductSearchInfo productSearchInfo = new WixProductSearchInfo(row); allSearches.Add(productSearchInfo.Id, productSearchInfo); } } // Merge in the variable/condition info and get the canonical ordering for // the searches. List<WixSearchInfo> orderedSearches = new List<WixSearchInfo>(); Table wixSearchTable = bundle.Tables["WixSearch"]; if (null != wixSearchTable && 0 < wixSearchTable.Rows.Count) { orderedSearches.Capacity = wixSearchTable.Rows.Count; foreach (Row row in wixSearchTable.Rows) { WixSearchInfo searchInfo = allSearches[(string)row[0]]; searchInfo.AddWixSearchRowInfo(row); orderedSearches.Add(searchInfo); } } // extract files that come from cabinet files (this does not extract files from merge modules) this.ExtractCabinets(cabinets); WixBundleRow bundleInfo = (WixBundleRow)bundleTable.Rows[0]; bundleInfo.PerMachine = true; // default to per-machine but the first-per user package would flip it. // Get update if specified. Table bundleUpdateTable = bundle.Tables["WixBundleUpdate"]; WixBundleUpdateRow bundleUpdateRow = null; if (null != bundleUpdateTable) { bundleUpdateRow = (WixBundleUpdateRow)bundleUpdateTable.Rows[0]; } // Get update registration if specified. Table updateRegistrationTable = bundle.Tables["WixUpdateRegistration"]; WixUpdateRegistrationRow updateRegistrationInfo = null; if (null != updateRegistrationTable) { updateRegistrationInfo = (WixUpdateRegistrationRow)updateRegistrationTable.Rows[0]; } // Get the explicit payloads. Table payloadTable = bundle.Tables["Payload"]; Dictionary<string, PayloadInfoRow> allPayloads = new Dictionary<string, PayloadInfoRow>(payloadTable.Rows.Count); Table payloadInfoTable = bundle.EnsureTable(core.TableDefinitions["PayloadInfo"]); foreach (PayloadInfoRow row in payloadInfoTable.Rows) { allPayloads.Add(row.Id, row); } RowDictionary<Row> payloadDisplayInformationRows = new RowDictionary<Row>(bundle.Tables["PayloadDisplayInformation"]); foreach (Row row in payloadTable.Rows) { string id = (string)row[0]; PayloadInfoRow payloadInfo = null; if (allPayloads.ContainsKey(id)) { payloadInfo = allPayloads[id]; } else { allPayloads.Add(id, payloadInfo = (PayloadInfoRow)payloadInfoTable.CreateRow(row.SourceLineNumbers)); } payloadInfo.FillFromPayloadRow(bundle, row); // Check if there is an override row for the display name or description. Row payloadDisplayInformationRow; if (payloadDisplayInformationRows.TryGet(id, out payloadDisplayInformationRow)) { if (!String.IsNullOrEmpty(payloadDisplayInformationRow[1] as string)) { payloadInfo.ProductName = (string)payloadDisplayInformationRow[1]; } if (!String.IsNullOrEmpty(payloadDisplayInformationRow[2] as string)) { payloadInfo.Description = (string)payloadDisplayInformationRow[2]; } } if (payloadInfo.Packaging == PackagingType.Unknown) { payloadInfo.Packaging = bundleInfo.DefaultPackagingType; } } Dictionary<string, ContainerInfo> containers = new Dictionary<string, ContainerInfo>(); Dictionary<string, bool> payloadsAddedToContainers = new Dictionary<string, bool>(); // Create the list of containers. Table containerTable = bundle.Tables["Container"]; if (null != containerTable) { foreach (Row row in containerTable.Rows) { ContainerInfo container = new ContainerInfo(row, this.FileManager); containers.Add(container.Id, container); } } // Create the default attached container for payloads that need to be attached but don't have an explicit container. ContainerInfo defaultAttachedContainer = new ContainerInfo("WixAttachedContainer", "bundle-attached.cab", "attached", null, this.FileManager); containers.Add(defaultAttachedContainer.Id, defaultAttachedContainer); Row baRow = baTable.Rows[0]; string baPayloadId = (string)baRow[0]; // Create lists of which payloads go in each container or are layout only. foreach (Row row in wixGroupTable.Rows) { string rowParentName = (string)row[0]; string rowParentType = (string)row[1]; string rowChildName = (string)row[2]; string rowChildType = (string)row[3]; if (Enum.GetName(typeof(ComplexReferenceChildType), ComplexReferenceChildType.Payload) == rowChildType) { PayloadInfoRow payload = allPayloads[rowChildName]; if (Enum.GetName(typeof(ComplexReferenceParentType), ComplexReferenceParentType.Container) == rowParentType) { ContainerInfo container = containers[rowParentName]; // Make sure the BA DLL is the first payload. if (payload.Id.Equals(baPayloadId)) { container.Payloads.Insert(0, payload); } else { container.Payloads.Add(payload); } payload.Container = container.Id; payloadsAddedToContainers.Add(rowChildName, false); } else if (Enum.GetName(typeof(ComplexReferenceParentType), ComplexReferenceParentType.Layout) == rowParentType) { payload.LayoutOnly = true; } } } ContainerInfo burnUXContainer; containers.TryGetValue(Compiler.BurnUXContainerId, out burnUXContainer); List<PayloadInfoRow> uxPayloads = null == burnUXContainer ? null : burnUXContainer.Payloads; // If we didn't get any UX payloads, it's an error! if (null == uxPayloads || 0 == uxPayloads.Count) { throw new WixException(WixErrors.MissingBundleInformation("BootstrapperApplication")); } // Get the catalog information Dictionary<string, CatalogInfo> catalogs = new Dictionary<string, CatalogInfo>(); Table catalogTable = bundle.Tables["WixCatalog"]; if (null != catalogTable) { foreach (WixCatalogRow catalogRow in catalogTable.Rows) { // Each catalog is also a payload string payloadId = Common.GenerateIdentifier("pay", true, catalogRow.SourceFile); string catalogFile = this.FileManager.ResolveFile(catalogRow.SourceFile, "Catalog", catalogRow.SourceLineNumbers, BindStage.Normal); PayloadInfoRow payloadInfo = PayloadInfoRow.Create(catalogRow.SourceLineNumbers, bundle, payloadId, Path.GetFileName(catalogFile), catalogFile, true, false, null, burnUXContainer.Id, PackagingType.Embedded); // Add the payload to the UX container allPayloads.Add(payloadInfo.Id, payloadInfo); burnUXContainer.Payloads.Add(payloadInfo); payloadsAddedToContainers.Add(payloadInfo.Id, true); // Create the catalog info CatalogInfo catalog = new CatalogInfo(catalogRow, payloadId); catalogs.Add(catalog.Id, catalog); } } // Get the chain packages, this may add more payloads. Dictionary<string, ChainPackageInfo> allPackages = new Dictionary<string, ChainPackageInfo>(); Dictionary<string, RollbackBoundaryInfo> allBoundaries = new Dictionary<string, RollbackBoundaryInfo>(); foreach (Row row in chainPackageTable.Rows) { Compiler.ChainPackageType type = (Compiler.ChainPackageType)Enum.Parse(typeof(Compiler.ChainPackageType), row[1].ToString(), true); if (Compiler.ChainPackageType.RollbackBoundary == type) { RollbackBoundaryInfo rollbackBoundary = new RollbackBoundaryInfo(row); allBoundaries.Add(rollbackBoundary.Id, rollbackBoundary); } else // package { Table chainPackageInfoTable = bundle.EnsureTable(this.core.TableDefinitions["ChainPackageInfo"]); ChainPackageInfo packageInfo = new ChainPackageInfo(row, wixGroupTable, allPayloads, containers, this.FileManager, this.core, bundle); allPackages.Add(packageInfo.Id, packageInfo); chainPackageInfoTable.Rows.Add(packageInfo); // Add package properties to resolve fields later. if (null != variableCache) { Binder.PopulatePackageVariableCache(packageInfo, variableCache); } } } // Determine patches to automatically slipstream. this.AutomaticallySlipstreamPatches(bundle, allPackages.Values); // NOTE: All payloads should be generated before here with the exception of specific engine and ux data files. ArrayList fileTransfers = new ArrayList(); string layoutDirectory = Path.GetDirectoryName(bundleFile); // Handle any payloads not explicitly in a container. foreach (string payloadName in allPayloads.Keys) { if (!payloadsAddedToContainers.ContainsKey(payloadName)) { PayloadInfoRow payload = allPayloads[payloadName]; if (PackagingType.Embedded == payload.Packaging) { payload.Container = defaultAttachedContainer.Id; defaultAttachedContainer.Payloads.Add(payload); } else if (!String.IsNullOrEmpty(payload.FullFileName)) { FileTransfer transfer; if (FileTransfer.TryCreate(payload.FullFileName, Path.Combine(layoutDirectory, payload.Name), false, "Payload", payload.SourceLineNumbers, out transfer)) { fileTransfers.Add(transfer); } } } } // Give the UX payloads their embedded IDs... for (int uxPayloadIndex = 0; uxPayloadIndex < uxPayloads.Count; ++uxPayloadIndex) { PayloadInfoRow payload = uxPayloads[uxPayloadIndex]; // In theory, UX payloads could be embedded in the UX CAB, external to the // bundle EXE, or even downloaded. The current engine requires the UX to be // fully present before any downloading starts, so that rules out downloading. // Also, the burn engine does not currently copy external UX payloads into // the temporary UX directory correctly, so we don't allow external either. if (PackagingType.Embedded != payload.Packaging) { core.OnMessage(WixWarnings.UxPayloadsOnlySupportEmbedding(payload.SourceLineNumbers, payload.FullFileName)); payload.Packaging = PackagingType.Embedded; } payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, uxPayloadIndex); } if (this.core.EncounteredError) { return false; } // If catalog files exist, non-UX payloads should validate with the catalog if (catalogs.Count > 0) { foreach (PayloadInfoRow payloadInfo in allPayloads.Values) { if (String.IsNullOrEmpty(payloadInfo.EmbeddedId)) { VerifyPayloadWithCatalog(payloadInfo, catalogs); } } } if (this.core.EncounteredError) { return false; } // 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. ChainInfo chain = new ChainInfo(chainTable.Rows[0]); // WixChain table always has one and only row in it. RollbackBoundaryInfo previousRollbackBoundary = new RollbackBoundaryInfo("WixDefaultBoundary"); // ensure there is always a rollback boundary at the beginning of the chain. foreach (Row row in wixGroupTable.Rows) { string rowParentName = (string)row[0]; string rowParentType = (string)row[1]; string rowChildName = (string)row[2]; string rowChildType = (string)row[3]; if ("PackageGroup" == rowParentType && "WixChain" == rowParentName && "Package" == rowChildType) { ChainPackageInfo packageInfo = null; if (allPackages.TryGetValue(rowChildName, out packageInfo)) { if (null != previousRollbackBoundary) { chain.RollbackBoundaries.Add(previousRollbackBoundary); packageInfo.RollbackBoundary = previousRollbackBoundary; previousRollbackBoundary = null; } chain.Packages.Add(packageInfo); } else // must be a rollback boundary. { // Discard the next rollback boundary if we have a previously defined boundary. Of course, // a boundary specifically defined will override the default boundary. RollbackBoundaryInfo nextRollbackBoundary = allBoundaries[rowChildName]; if (null != previousRollbackBoundary && !previousRollbackBoundary.Default) { this.core.OnMessage(WixWarnings.DiscardedRollbackBoundary(nextRollbackBoundary.SourceLineNumbers, nextRollbackBoundary.Id)); } else { previousRollbackBoundary = nextRollbackBoundary; } } } } if (null != previousRollbackBoundary) { this.core.OnMessage(WixWarnings.DiscardedRollbackBoundary(previousRollbackBoundary.SourceLineNumbers, previousRollbackBoundary.Id)); } // 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; ChainPackageInfo previousPackage = null; foreach (ChainPackageInfo package in chain.Packages) { if (null != package.RollbackBoundary) { if (null != previousPackage) { previousPackage.RollbackBoundaryBackwardId = previousRollbackBoundaryId; } previousRollbackBoundaryId = package.RollbackBoundary.Id; } previousPackage = package; } if (!String.IsNullOrEmpty(previousRollbackBoundaryId) && null != previousPackage) { previousPackage.RollbackBoundaryBackwardId = previousRollbackBoundaryId; } // Give all embedded payloads that don't have an embedded ID yet an embedded ID. int payloadIndex = 0; foreach (PayloadInfoRow payload in allPayloads.Values) { Debug.Assert(PackagingType.Unknown != payload.Packaging); if (PackagingType.Embedded == payload.Packaging && String.IsNullOrEmpty(payload.EmbeddedId)) { payload.EmbeddedId = String.Format(CultureInfo.InvariantCulture, BurnCommon.BurnAttachedContainerEmbeddedIdFormat, payloadIndex); ++payloadIndex; } } // Load the MsiProperty information... Table msiPropertyTable = bundle.Tables["MsiProperty"]; if (null != msiPropertyTable && 0 < msiPropertyTable.Rows.Count) { foreach (Row row in msiPropertyTable.Rows) { MsiPropertyInfo msiProperty = new MsiPropertyInfo(row); ChainPackageInfo package; if (allPackages.TryGetValue(msiProperty.PackageId, out package)) { package.MsiProperties.Add(msiProperty); } else { core.OnMessage(WixErrors.IdentifierNotFound("Package", msiProperty.PackageId)); } } } // Load the SlipstreamMsp information... Table slipstreamMspTable = bundle.Tables["SlipstreamMsp"]; if (null != slipstreamMspTable && 0 < slipstreamMspTable.Rows.Count) { foreach (Row row in slipstreamMspTable.Rows) { string msiPackageId = (string)row[0]; string mspPackageId = (string)row[1]; if (!allPackages.ContainsKey(mspPackageId)) { core.OnMessage(WixErrors.IdentifierNotFound("Package", mspPackageId)); continue; } ChainPackageInfo package; if (!allPackages.TryGetValue(msiPackageId, out package)) { core.OnMessage(WixErrors.IdentifierNotFound("Package", msiPackageId)); continue; } package.SlipstreamMsps.Add(mspPackageId); } } // Load the ExitCode information... Table exitCodeTable = bundle.Tables["ExitCode"]; if (null != exitCodeTable && 0 < exitCodeTable.Rows.Count) { foreach (Row row in exitCodeTable.Rows) { ExitCodeInfo exitCode = new ExitCodeInfo(row); ChainPackageInfo package; if (allPackages.TryGetValue(exitCode.PackageId, out package)) { package.ExitCodes.Add(exitCode); } else { core.OnMessage(WixErrors.IdentifierNotFound("Package", exitCode.PackageId)); } } } // Load the CommandLine information... Dictionary<string, List<WixCommandLineRow>> commandLinesByPackage = new Dictionary<string, List<WixCommandLineRow>>(); Table commandLineTable = bundle.Tables["WixCommandLine"]; if (null != commandLineTable && 0 < commandLineTable.Rows.Count) { foreach (WixCommandLineRow row in commandLineTable.Rows) { if (!commandLinesByPackage.ContainsKey(row.PackageId)) { commandLinesByPackage.Add(row.PackageId, new List<WixCommandLineRow>()); } List<WixCommandLineRow> commandLines = commandLinesByPackage[row.PackageId]; commandLines.Add(row); } } // Resolve any delayed fields before generating the manifest. if (0 < delayedFields.Count) { this.ResolveDelayedFields(bundle, delayedFields, variableCache, null); } // Process WixApprovedExeForElevation rows. Table wixApprovedExeForElevationTable = bundle.Tables["WixApprovedExeForElevation"]; List<ApprovedExeForElevation> approvedExesForElevation = new List<ApprovedExeForElevation>(); if (null != wixApprovedExeForElevationTable && 0 < wixApprovedExeForElevationTable.Rows.Count) { foreach (WixApprovedExeForElevationRow wixApprovedExeForElevationRow in wixApprovedExeForElevationTable.Rows) { ApprovedExeForElevation approvedExeForElevation = new ApprovedExeForElevation(wixApprovedExeForElevationRow); approvedExesForElevation.Add(approvedExeForElevation); } } // Set the overridable bundle provider key. this.SetBundleProviderKey(bundle, bundleInfo); // Import or generate dependency providers for packages in the manifest. this.ProcessDependencyProviders(bundle, allPackages); // Generate the core-defined BA manifest tables... this.GenerateBAManifestPackageTables(bundle, chain.Packages); this.GenerateBAManifestPayloadTables(bundle, chain.Packages, allPayloads); foreach (BinderExtension extension in this.extensions) { extension.BundleFinalize(bundle); } // Start creating the bundle. this.PopulateBundleInfoFromChain(bundleInfo, chain.Packages); this.PopulateChainInfoTables(bundle, bundleInfo, chain.Packages); this.GenerateBAManifestBundleTables(bundle, bundleInfo); // Copy the burn.exe to a writable location then mark it to be moved to its // final build location. string stubPlatform; if (Platform.X64 == bundleInfo.Platform) // today, the x64 Burn uses the x86 stub. { stubPlatform = "x86"; } else { stubPlatform = bundleInfo.Platform.ToString(); } string wixExeDirectory = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), stubPlatform); string stubFile = Path.Combine(wixExeDirectory, "burn.exe"); string bundleTempPath = Path.Combine(this.TempFilesLocation, Path.GetFileName(bundleFile)); this.core.OnMessage(WixVerboses.GeneratingBundle(bundleTempPath, stubFile)); File.Copy(stubFile, bundleTempPath, true); File.SetAttributes(bundleTempPath, FileAttributes.Normal); FileTransfer bundleTransfer; if (FileTransfer.TryCreate(bundleTempPath, bundleFile, true, "Bundle", bundleInfo.SourceLineNumbers, out bundleTransfer)) { bundleTransfer.Built = true; fileTransfers.Add(bundleTransfer); } // Create our manifests, CABs and final EXE... string baManifestPath = Path.Combine(this.TempFilesLocation, "bundle-BootstrapperApplicationData.xml"); this.CreateBootstrapperApplicationManifest(bundle, baManifestPath, uxPayloads); // Add the bootstrapper application manifest to the set of UX payloads. PayloadInfoRow baManifestPayload = PayloadInfoRow.Create(null /*TODO*/, bundle, Common.GenerateIdentifier("ux", true, "BootstrapperApplicationData.xml"), "BootstrapperApplicationData.xml", baManifestPath, false, true, null, burnUXContainer.Id, PackagingType.Embedded); baManifestPayload.EmbeddedId = string.Format(CultureInfo.InvariantCulture, BurnCommon.BurnUXContainerEmbeddedIdFormat, uxPayloads.Count); uxPayloads.Add(baManifestPayload); // Create all the containers except the UX container first so the manifest in the UX container can contain all size and hash information. foreach (ContainerInfo container in containers.Values) { if (Compiler.BurnUXContainerId != container.Id && 0 < container.Payloads.Count) { this.CreateContainer(container, null); } } string manifestPath = Path.Combine(this.TempFilesLocation, "bundle-manifest.xml"); this.CreateBurnManifest(bundleFile, bundleInfo, bundleUpdateRow, updateRegistrationInfo, manifestPath, allRelatedBundles, allVariables, orderedSearches, allPayloads, chain, containers, catalogs, bundle.Tables["WixBundleTag"], approvedExesForElevation, commandLinesByPackage); this.UpdateBurnResources(bundleTempPath, bundleFile, bundleInfo); // update the .wixburn section to point to at the UX and attached container(s) then attach the container(s) if they should be attached. using (BurnWriter writer = BurnWriter.Open(bundleTempPath, this.core)) { FileInfo burnStubFile = new FileInfo(bundleTempPath); writer.InitializeBundleSectionData(burnStubFile.Length, bundleInfo.BundleId); // Always create UX container and attach it first this.CreateContainer(burnUXContainer, manifestPath); writer.AppendContainer(burnUXContainer.TempPath, BurnWriter.Container.UX); // Now append all other attached containers foreach (ContainerInfo container in containers.Values) { if (container.Type == "attached") { // The container was only created if it had payloads. if (Compiler.BurnUXContainerId != container.Id && 0 < container.Payloads.Count) { writer.AppendContainer(container.TempPath, BurnWriter.Container.Attached); } } } } // Output the bundle to a file if (null != this.pdbFile) { Pdb pdb = new Pdb(null); pdb.Output = bundle; pdb.Save(this.pdbFile, null, this.WixVariableResolver, this.TempFilesLocation); } // Add detached containers to the list of file transfers. foreach (ContainerInfo container in containers.Values) { if ("detached" == container.Type) { FileTransfer transfer; if (FileTransfer.TryCreate(Path.Combine(this.TempFilesLocation, container.Name), Path.Combine(layoutDirectory, container.Name), true, "Container", container.SourceLineNumbers, out transfer)) { transfer.Built = true; fileTransfers.Add(transfer); } } } // layout media string bundleFilename = Path.GetFileName(bundleFile); if ("setup.exe".Equals(bundleFilename, StringComparison.OrdinalIgnoreCase)) { this.core.OnMessage(WixErrors.InsecureBundleFilename(bundleFilename)); } try { this.core.OnMessage(WixVerboses.LayingOutMedia()); this.LayoutMedia(fileTransfers, this.suppressAclReset); } finally { if (!String.IsNullOrEmpty(this.contentsFile)) { this.CreateContentsFile(this.contentsFile, allPayloads.Values); } if (!String.IsNullOrEmpty(this.outputsFile)) { this.CreateOutputsFile(this.outputsFile, fileTransfers, this.pdbFile); } if (!String.IsNullOrEmpty(this.builtOutputsFile)) { this.CreateBuiltOutputsFile(this.builtOutputsFile, fileTransfers, this.pdbFile); } } return !this.core.EncounteredError; }