/// <param name="output">Output to generate image for.</param> /// <param name="fileTransfers">Array of files to be transfered.</param> /// <param name="layoutDirectory">The directory in which the image should be layed out.</param> /// <param name="compressed">Flag if source image should be compressed.</param> /// <returns>The uncompressed file rows.</returns> public void Execute() { RowDictionary <WixMediaRow> wixMediaRows = new RowDictionary <WixMediaRow>(this.WixMediaTable); this.lastCabinetAddedToMediaTable = new Dictionary <string, string>(); this.SetCabbingThreadCount(); // Send Binder object to Facilitate NewCabNamesCallBack Callback CabinetBuilder cabinetBuilder = new CabinetBuilder(this.CabbingThreadCount, Marshal.GetFunctionPointerForDelegate(this.newCabNamesCallBack)); // Supply Compile MediaTemplate Attributes to Cabinet Builder int MaximumCabinetSizeForLargeFileSplitting; int MaximumUncompressedMediaSize; this.GetMediaTemplateAttributes(out MaximumCabinetSizeForLargeFileSplitting, out MaximumUncompressedMediaSize); cabinetBuilder.MaximumCabinetSizeForLargeFileSplitting = MaximumCabinetSizeForLargeFileSplitting; cabinetBuilder.MaximumUncompressedMediaSize = MaximumUncompressedMediaSize; foreach (var entry in this.FileRowsByCabinet) { MediaRow mediaRow = entry.Key; IEnumerable <FileFacade> files = entry.Value; CompressionLevel compressionLevel = this.DefaultCompressionLevel; WixMediaRow wixMediaRow = null; string mediaLayoutFolder = null; if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) { mediaLayoutFolder = wixMediaRow.Layout; if (wixMediaRow.CompressionLevel.HasValue) { compressionLevel = wixMediaRow.CompressionLevel.Value; } } string cabinetDir = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); CabinetWorkItem cabinetWorkItem = this.CreateCabinetWorkItem(this.Output, cabinetDir, mediaRow, compressionLevel, files, this.fileTransfers); if (null != cabinetWorkItem) { cabinetBuilder.Enqueue(cabinetWorkItem); } } // stop processing if an error previously occurred if (Messaging.Instance.EncounteredError) { return; } // create queued cabinets with multiple threads cabinetBuilder.CreateQueuedCabinets(); if (Messaging.Instance.EncounteredError) { return; } }
/// <summary> /// Adds a row to the media table with cab name template filled in. /// </summary> /// <param name="mediaTable"></param> /// <param name="cabIndex"></param> /// <returns></returns> private MediaRow AddMediaRow(WixMediaTemplateRow mediaTemplateRow, Table mediaTable, int cabIndex) { MediaRow currentMediaRow = (MediaRow)mediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); currentMediaRow.DiskId = cabIndex; currentMediaRow.Cabinet = String.Format(CultureInfo.InvariantCulture, this.CabinetNameTemplate, cabIndex); Table wixMediaTable = this.Output.EnsureTable(this.TableDefinitions["WixMedia"]); WixMediaRow row = (WixMediaRow)wixMediaTable.CreateRow(mediaTemplateRow.SourceLineNumbers); row.DiskId = cabIndex; row.CompressionLevel = mediaTemplateRow.CompressionLevel; return(currentMediaRow); }
public void Execute() { Dictionary <MediaRow, List <FileFacade> > filesByCabinetMedia = new Dictionary <MediaRow, List <FileFacade> >(); RowDictionary <MediaRow> mediaRows = new RowDictionary <MediaRow>(); List <FileFacade> uncompressedFiles = new List <FileFacade>(); MediaRow mergeModuleMediaRow = null; Table mediaTable = this.Output.Tables["Media"]; Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; // If both tables are authored, it is an error. if ((mediaTemplateTable != null && mediaTemplateTable.Rows.Count > 0) && (mediaTable != null && mediaTable.Rows.Count > 1)) { throw new WixException(WixErrors.MediaTableCollision(null)); } // When building merge module, all the files go to "#MergeModule.CABinet". if (OutputType.Module == this.Output.Type) { Table mergeModuleMediaTable = new Table(null, this.TableDefinitions["Media"]); mergeModuleMediaRow = (MediaRow)mergeModuleMediaTable.CreateRow(null); mergeModuleMediaRow.Cabinet = "#MergeModule.CABinet"; filesByCabinetMedia.Add(mergeModuleMediaRow, new List <FileFacade>()); } if (OutputType.Module == this.Output.Type || null == mediaTemplateTable) { this.ManuallyAssignFiles(mediaTable, mergeModuleMediaRow, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); } else { this.AutoAssignFiles(mediaTable, this.FileFacades, filesByCabinetMedia, mediaRows, uncompressedFiles); } this.FileFacadesByCabinetMedia = new Dictionary <MediaRow, IEnumerable <FileFacade> >(); foreach (var mediaRowWithFiles in filesByCabinetMedia) { this.FileFacadesByCabinetMedia.Add(mediaRowWithFiles.Key, mediaRowWithFiles.Value); } this.MediaRows = mediaRows; this.UncompressedFileFacades = uncompressedFiles; }
/// <summary> /// Call back to Add File Transfer for new Cab and add new Cab to Media table /// This callback can come from Multiple Cabinet Builder Threads and so should be thread safe /// This callback will not be called in case there is no File splitting. i.e. MaximumCabinetSizeForLargeFileSplitting was not authored /// </summary> /// <param name="firstCabName">The name of splitting cabinet without extention e.g. "cab1".</param> /// <param name="newCabName">The name of the new cabinet that would be formed by splitting e.g. "cab1b.cab"</param> /// <param name="fileToken">The file token of the first file present in the splitting cabinet</param> internal void NewCabNamesCallBack([MarshalAs(UnmanagedType.LPWStr)] string firstCabName, [MarshalAs(UnmanagedType.LPWStr)] string newCabName, [MarshalAs(UnmanagedType.LPWStr)] string fileToken) { // Locking Mutex here as this callback can come from Multiple Cabinet Builder Threads Mutex mutex = new Mutex(false, "WixCabinetSplitBinderCallback"); try { if (!mutex.WaitOne(0, false)) // Check if you can get the lock { // Cound not get the Lock Messaging.Instance.OnMessage(WixVerboses.CabinetsSplitInParallel()); mutex.WaitOne(); // Wait on other thread } string firstCabinetName = firstCabName + ".cab"; string newCabinetName = newCabName; bool transferAdded = false; // Used for Error Handling // Create File Transfer for new Cabinet using transfer of Base Cabinet foreach (FileTransfer transfer in this.FileTransfers) { if (firstCabinetName.Equals(Path.GetFileName(transfer.Source), StringComparison.InvariantCultureIgnoreCase)) { string newCabSourcePath = Path.Combine(Path.GetDirectoryName(transfer.Source), newCabinetName); string newCabTargetPath = Path.Combine(Path.GetDirectoryName(transfer.Destination), newCabinetName); FileTransfer newTransfer; if (FileTransfer.TryCreate(newCabSourcePath, newCabTargetPath, transfer.Move, "Cabinet", transfer.SourceLineNumbers, out newTransfer)) { newTransfer.Built = true; this.fileTransfers.Add(newTransfer); transferAdded = true; break; } } } // Check if File Transfer was added if (!transferAdded) { throw new WixException(WixErrors.SplitCabinetCopyRegistrationFailed(newCabinetName, firstCabinetName)); } // Add the new Cabinets to media table using LastSequence of Base Cabinet Table mediaTable = this.Output.Tables["Media"]; Table wixFileTable = this.Output.Tables["WixFile"]; int diskIDForLastSplitCabAdded = 0; // The DiskID value for the first cab in this cabinet split chain int lastSequenceForLastSplitCabAdded = 0; // The LastSequence value for the first cab in this cabinet split chain bool lastSplitCabinetFound = false; // Used for Error Handling string lastCabinetOfThisSequence = String.Empty; // Get the Value of Last Cabinet Added in this split Sequence from Dictionary if (!this.lastCabinetAddedToMediaTable.TryGetValue(firstCabinetName, out lastCabinetOfThisSequence)) { // If there is no value for this sequence, then use first Cabinet is the last one of this split sequence lastCabinetOfThisSequence = firstCabinetName; } foreach (MediaRow mediaRow in mediaTable.Rows) { // Get details for the Last Cabinet Added in this Split Sequence if ((lastSequenceForLastSplitCabAdded == 0) && lastCabinetOfThisSequence.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) { lastSequenceForLastSplitCabAdded = mediaRow.LastSequence; diskIDForLastSplitCabAdded = mediaRow.DiskId; lastSplitCabinetFound = true; } // Check for Name Collision for the new Cabinet added if (newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) { // Name Collision of generated Split Cabinet Name and user Specified Cab name for current row throw new WixException(WixErrors.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); } } // Check if the last Split Cabinet was found in the Media Table if (!lastSplitCabinetFound) { throw new WixException(WixErrors.SplitCabinetInsertionFailed(newCabinetName, firstCabinetName, lastCabinetOfThisSequence)); } // The new Row has to be inserted just after the last cab in this cabinet split chain according to DiskID Sort // This is because the FDI Extract requires DiskID of Split Cabinets to be continuous. It Fails otherwise with // Error 2350 (FDI Server Error) as next DiskID did not have the right split cabinet during extraction MediaRow newMediaRow = (MediaRow)mediaTable.CreateRow(null); newMediaRow.Cabinet = newCabinetName; newMediaRow.DiskId = diskIDForLastSplitCabAdded + 1; // When Sorted with DiskID, this new Cabinet Row is an Insertion newMediaRow.LastSequence = lastSequenceForLastSplitCabAdded; // Now increment the DiskID for all rows that come after the newly inserted row to Ensure that DiskId is unique foreach (MediaRow mediaRow in mediaTable.Rows) { // Check if this row comes after inserted row and it is not the new cabinet inserted row if (mediaRow.DiskId >= newMediaRow.DiskId && !newCabinetName.Equals(mediaRow.Cabinet, StringComparison.InvariantCultureIgnoreCase)) { mediaRow.DiskId++; // Increment DiskID } } // Now Increment DiskID for All files Rows so that they refer to the right Media Row foreach (WixFileRow wixFileRow in wixFileTable.Rows) { // Check if this row comes after inserted row and if this row is not the file that has to go into the current cabinet // This check will work as we have only one large file in every splitting cabinet // If we want to support splitting cabinet with more large files we need to update this code if (wixFileRow.DiskId >= newMediaRow.DiskId && !wixFileRow.File.Equals(fileToken, StringComparison.InvariantCultureIgnoreCase)) { wixFileRow.DiskId++; // Increment DiskID } } // Update the Last Cabinet Added in the Split Sequence in Dictionary for future callback this.lastCabinetAddedToMediaTable[firstCabinetName] = newCabinetName; mediaTable.ValidateRows(); // Valdiates DiskDIs, throws Exception as Wix Error if validation fails } finally { // Releasing the Mutex here mutex.ReleaseMutex(); } }
/// <summary> /// Creates a work item to create a cabinet. /// </summary> /// <param name="output">Output for the current database.</param> /// <param name="cabinetDir">Directory to create cabinet in.</param> /// <param name="mediaRow">MediaRow containing information about the cabinet.</param> /// <param name="fileFacades">Collection of files in this cabinet.</param> /// <param name="fileTransfers">Array of files to be transfered.</param> /// <returns>created CabinetWorkItem object</returns> private CabinetWorkItem CreateCabinetWorkItem(Output output, string cabinetDir, MediaRow mediaRow, CompressionLevel compressionLevel, IEnumerable <FileFacade> fileFacades, List <FileTransfer> fileTransfers) { CabinetWorkItem cabinetWorkItem = null; string tempCabinetFileX = Path.Combine(this.TempFilesLocation, mediaRow.Cabinet); // check for an empty cabinet if (!fileFacades.Any()) { string cabinetName = mediaRow.Cabinet; // remove the leading '#' from the embedded cabinet name to make the warning easier to understand if (cabinetName.StartsWith("#", StringComparison.Ordinal)) { cabinetName = cabinetName.Substring(1); } // If building a patch, remind them to run -p for torch. if (OutputType.Patch == output.Type) { Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName, true)); } else { Messaging.Instance.OnMessage(WixWarnings.EmptyCabinet(mediaRow.SourceLineNumbers, cabinetName)); } } ResolvedCabinet resolvedCabinet = this.ResolveCabinet(tempCabinetFileX, fileFacades); // create a cabinet work item if it's not being skipped if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption) { int maxThreshold = 0; // default to the threshold for best smartcabbing (makes smallest cabinet). cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold, compressionLevel /*, this.FileManager*/); } else // reuse the cabinet from the cabinet cache. { Messaging.Instance.OnMessage(WixVerboses.ReusingCabCache(mediaRow.SourceLineNumbers, mediaRow.Cabinet, resolvedCabinet.Path)); try { // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output) // causing the project to look like it perpetually needs a rebuild until all of the reused // cabinets get newer timestamps. File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now); } catch (Exception e) { Messaging.Instance.OnMessage(WixWarnings.CannotUpdateCabCache(mediaRow.SourceLineNumbers, resolvedCabinet.Path, e.Message)); } } if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal)) { Table streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]); Row streamRow = streamsTable.CreateRow(mediaRow.SourceLineNumbers); streamRow[0] = mediaRow.Cabinet.Substring(1); streamRow[1] = resolvedCabinet.Path; } else { string destinationPath = Path.Combine(cabinetDir, mediaRow.Cabinet); FileTransfer transfer; if (FileTransfer.TryCreate(resolvedCabinet.Path, destinationPath, CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption, "Cabinet", mediaRow.SourceLineNumbers, out transfer)) { transfer.Built = true; fileTransfers.Add(transfer); } } return(cabinetWorkItem); }
/// <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); }
/// <summary> /// Assign files to cabinets based on Media authoring. /// </summary> /// <param name="mediaTable"></param> /// <param name="mergeModuleMediaRow"></param> /// <param name="fileFacades"></param> private void ManuallyAssignFiles(Table mediaTable, MediaRow mergeModuleMediaRow, IEnumerable <FileFacade> fileFacades, Dictionary <MediaRow, List <FileFacade> > filesByCabinetMedia, RowDictionary <MediaRow> mediaRows, List <FileFacade> uncompressedFiles) { if (OutputType.Module != this.Output.Type) { if (null != mediaTable) { Dictionary <string, MediaRow> cabinetMediaRows = new Dictionary <string, MediaRow>(StringComparer.InvariantCultureIgnoreCase); foreach (MediaRow mediaRow in mediaTable.Rows) { // If the Media row has a cabinet, make sure it is unique across all Media rows. if (!String.IsNullOrEmpty(mediaRow.Cabinet)) { MediaRow existingRow; if (cabinetMediaRows.TryGetValue(mediaRow.Cabinet, out existingRow)) { Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName(mediaRow.SourceLineNumbers, mediaRow.Cabinet)); Messaging.Instance.OnMessage(WixErrors.DuplicateCabinetName2(existingRow.SourceLineNumbers, existingRow.Cabinet)); } else { cabinetMediaRows.Add(mediaRow.Cabinet, mediaRow); } } mediaRows.Add(mediaRow); } } foreach (MediaRow mediaRow in mediaRows.Values) { if (null != mediaRow.Cabinet) { filesByCabinetMedia.Add(mediaRow, new List <FileFacade>()); } } } foreach (FileFacade facade in fileFacades) { if (OutputType.Module == this.Output.Type) { filesByCabinetMedia[mergeModuleMediaRow].Add(facade); } else { MediaRow mediaRow; if (!mediaRows.TryGetValue(facade.WixFile.DiskId.ToString(CultureInfo.InvariantCulture), out mediaRow)) { Messaging.Instance.OnMessage(WixErrors.MissingMedia(facade.File.SourceLineNumbers, facade.WixFile.DiskId)); continue; } // When building a product, if the current file is not to be compressed or if // the package set not to be compressed, don't cab it. if (OutputType.Product == this.Output.Type && (YesNoType.No == facade.File.Compressed || (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) { uncompressedFiles.Add(facade); } else // file is marked compressed. { List <FileFacade> cabinetFiles; if (filesByCabinetMedia.TryGetValue(mediaRow, out cabinetFiles)) { cabinetFiles.Add(facade); } else { Messaging.Instance.OnMessage(WixErrors.ExpectedMediaCabinet(facade.File.SourceLineNumbers, facade.File.File, facade.WixFile.DiskId)); } } } } }
/// <summary> /// Assign files to cabinets based on MediaTemplate authoring. /// </summary> /// <param name="fileFacades">FileRowCollection</param> private void AutoAssignFiles(Table mediaTable, IEnumerable <FileFacade> fileFacades, Dictionary <MediaRow, List <FileFacade> > filesByCabinetMedia, RowDictionary <MediaRow> mediaRows, List <FileFacade> uncompressedFiles) { const int MaxCabIndex = 999; ulong currentPreCabSize = 0; ulong maxPreCabSizeInBytes; int maxPreCabSizeInMB = 0; int currentCabIndex = 0; MediaRow currentMediaRow = null; Table mediaTemplateTable = this.Output.Tables["WixMediaTemplate"]; // Auto assign files to cabinets based on maximum uncompressed media size mediaTable.Rows.Clear(); WixMediaTemplateRow mediaTemplateRow = (WixMediaTemplateRow)mediaTemplateTable.Rows[0]; if (!String.IsNullOrEmpty(mediaTemplateRow.CabinetTemplate)) { this.CabinetNameTemplate = mediaTemplateRow.CabinetTemplate; } string mumsString = Environment.GetEnvironmentVariable("WIX_MUMS"); try { // Override authored mums value if environment variable is authored. if (!String.IsNullOrEmpty(mumsString)) { maxPreCabSizeInMB = Int32.Parse(mumsString); } else { maxPreCabSizeInMB = mediaTemplateRow.MaximumUncompressedMediaSize; } maxPreCabSizeInBytes = (ulong)maxPreCabSizeInMB * 1024 * 1024; } catch (FormatException) { throw new WixException(WixErrors.IllegalEnvironmentVariable("WIX_MUMS", mumsString)); } catch (OverflowException) { throw new WixException(WixErrors.MaximumUncompressedMediaSizeTooLarge(null, maxPreCabSizeInMB)); } foreach (FileFacade facade in this.FileFacades) { // When building a product, if the current file is not to be compressed or if // the package set not to be compressed, don't cab it. if (OutputType.Product == this.Output.Type && (YesNoType.No == facade.File.Compressed || (YesNoType.NotSet == facade.File.Compressed && !this.FilesCompressed))) { uncompressedFiles.Add(facade); continue; } if (currentCabIndex == MaxCabIndex) { // Associate current file with last cab (irrespective of the size) and cab index is not incremented anymore. List <FileFacade> cabinetFiles = filesByCabinetMedia[currentMediaRow]; facade.WixFile.DiskId = currentCabIndex; cabinetFiles.Add(facade); continue; } // Update current cab size. currentPreCabSize += (ulong)facade.File.FileSize; if (currentPreCabSize > maxPreCabSizeInBytes) { // Overflow due to current file currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); mediaRows.Add(currentMediaRow); filesByCabinetMedia.Add(currentMediaRow, new List <FileFacade>()); List <FileFacade> cabinetFileRows = filesByCabinetMedia[currentMediaRow]; facade.WixFile.DiskId = currentCabIndex; cabinetFileRows.Add(facade); // Now files larger than MaxUncompressedMediaSize will be the only file in its cabinet so as to respect MaxUncompressedMediaSize currentPreCabSize = (ulong)facade.File.FileSize; } else { // File fits in the current cab. if (currentMediaRow == null) { // Create new cab and MediaRow currentMediaRow = this.AddMediaRow(mediaTemplateRow, mediaTable, ++currentCabIndex); mediaRows.Add(currentMediaRow); filesByCabinetMedia.Add(currentMediaRow, new List <FileFacade>()); } // Associate current file with current cab. List <FileFacade> cabinetFiles = filesByCabinetMedia[currentMediaRow]; facade.WixFile.DiskId = currentCabIndex; cabinetFiles.Add(facade); } } // If there are uncompressed files and no MediaRow, create a default one. if (uncompressedFiles.Count > 0 && mediaTable.Rows.Count == 0) { MediaRow defaultMediaRow = (MediaRow)mediaTable.CreateRow(null); defaultMediaRow.DiskId = 1; mediaRows.Add(defaultMediaRow); } }
public void Execute() { Debug.Assert(OutputType.Patch != this.Output.Type); List <FileFacade> allFileRows = this.CopyOutFileRows ? new List <FileFacade>() : null; #if REVISIT_FOR_PATCHING // TODO: Fix this patching related code to work correctly with FileFacades. bool copyToPatch = (allFileRows != null); bool copyFromPatch = !copyToPatch; RowDictionary <MediaRow> patchMediaRows = new RowDictionary <MediaRow>(); Dictionary <int, RowDictionary <WixFileRow> > patchMediaFileRows = new Dictionary <int, RowDictionary <WixFileRow> >(); Table patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); Table patchFileTable = this.Output.EnsureTable(this.TableDefinitions["WixFile"]); if (copyFromPatch) { // index patch files by diskId+fileId foreach (WixFileRow patchFileRow in patchFileTable.Rows) { int diskId = patchFileRow.DiskId; RowDictionary <WixFileRow> mediaFileRows; if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } mediaFileRows.Add(patchFileRow); } Table patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); patchMediaRows = new RowDictionary <MediaRow>(patchMediaTable); } // index paired transforms Dictionary <string, Output> pairedTransforms = new Dictionary <string, Output>(); foreach (SubStorage substorage in this.Output.SubStorages) { if (substorage.Name.StartsWith("#")) { pairedTransforms.Add(substorage.Name.Substring(1), substorage.Data); } } try { // copy File bind data into substorages foreach (SubStorage substorage in this.Output.SubStorages) { if (substorage.Name.StartsWith("#")) { // no changes necessary for paired transforms continue; } Output mainTransform = substorage.Data; Table mainWixFileTable = mainTransform.Tables["WixFile"]; Table mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; this.FileManagerCore.ActiveSubStorage = substorage; RowDictionary <WixFileRow> mainWixFiles = new RowDictionary <WixFileRow>(mainWixFileTable); RowDictionary <Row> mainMsiFileHashIndex = new RowDictionary <Row>(); Table mainFileTable = mainTransform.Tables["File"]; Output pairedTransform = (Output)pairedTransforms[substorage.Name]; // copy Media.LastSequence and index the MsiFileHash table if it exists. if (copyFromPatch) { Table pairedMediaTable = pairedTransform.Tables["Media"]; foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) { MediaRow patchMediaRow = patchMediaRows.Get(pairedMediaRow.DiskId); pairedMediaRow.Fields[1] = patchMediaRow.Fields[1]; } if (null != mainMsiFileHashTable) { mainMsiFileHashIndex = new RowDictionary <Row>(mainMsiFileHashTable); } // Validate file row changes for keypath-related issues this.ValidateFileRowChanges(mainTransform); } // Index File table of pairedTransform Table pairedFileTable = pairedTransform.Tables["File"]; RowDictionary <FileRow> pairedFileRows = new RowDictionary <FileRow>(pairedFileTable); if (null != mainFileTable) { if (copyFromPatch) { // Remove the MsiFileHash table because it will be updated later with the final file hash for each file mainTransform.Tables.Remove("MsiFileHash"); } foreach (FileRow mainFileRow in mainFileTable.Rows) { if (RowOperation.Delete == mainFileRow.Operation) { continue; } else if (RowOperation.None == mainFileRow.Operation && !copyToPatch) { continue; } WixFileRow mainWixFileRow = mainWixFiles.Get(mainFileRow.File); if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. { ObjectField objectField = (ObjectField)mainWixFileRow.Fields[6]; FileRow pairedFileRow = pairedFileRows.Get(mainFileRow.File); // If the file is new, we always need to add it to the patch. if (mainFileRow.Operation != RowOperation.Add) { // If PreviousData doesn't exist, target and upgrade layout point to the same location. No need to compare. if (null == objectField.PreviousData) { if (mainFileRow.Operation == RowOperation.None) { continue; } } else { // TODO: should this entire condition be placed in the binder file manager? if ((0 == (PatchAttributeType.Ignore & mainWixFileRow.PatchAttributes)) && !this.CompareFiles(objectField.PreviousData.ToString(), objectField.Data.ToString())) { // If the file is different, we need to mark the mainFileRow and pairedFileRow as modified. mainFileRow.Operation = RowOperation.Modify; if (null != pairedFileRow) { // Always patch-added, but never non-compressed. pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Modify; } } else { // The File is same. We need mark all the attributes as unchanged. mainFileRow.Operation = RowOperation.None; foreach (Field field in mainFileRow.Fields) { field.Modified = false; } if (null != pairedFileRow) { pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesPatchAdded; pairedFileRow.Fields[6].Modified = false; pairedFileRow.Operation = RowOperation.None; } continue; } } } else if (null != pairedFileRow) // RowOperation.Add { // Always patch-added, but never non-compressed. pairedFileRow.Attributes |= MsiInterop.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Add; } } // index patch files by diskId+fileId int diskId = mainWixFileRow.DiskId; RowDictionary <WixFileRow> mediaFileRows; if (!patchMediaFileRows.TryGetValue(diskId, out mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } string fileId = mainFileRow.File; WixFileRow patchFileRow = mediaFileRows.Get(fileId); if (copyToPatch) { if (null == patchFileRow) { FileRow patchActualFileRow = (FileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); patchActualFileRow.CopyFrom(mainFileRow); patchFileRow = (WixFileRow)patchFileTable.CreateRow(mainFileRow.SourceLineNumbers); patchFileRow.CopyFrom(mainWixFileRow); mediaFileRows.Add(patchFileRow); allFileRows.Add(new FileFacade(patchActualFileRow, patchFileRow, null)); // TODO: should we be passing along delta information? Probably, right? } else { // TODO: confirm the rest of data is identical? // make sure Source is same. Otherwise we are silently ignoring a file. if (0 != String.Compare(patchFileRow.Source, mainWixFileRow.Source, StringComparison.OrdinalIgnoreCase)) { Messaging.Instance.OnMessage(WixErrors.SameFileIdDifferentSource(mainFileRow.SourceLineNumbers, fileId, patchFileRow.Source, mainWixFileRow.Source)); } // capture the previous file versions (and associated data) from this targeted instance of the baseline into the current filerow. patchFileRow.AppendPreviousDataFrom(mainWixFileRow); } } else { // copy data from the patch back to the transform if (null != patchFileRow) { FileRow pairedFileRow = (FileRow)pairedFileRows.Get(fileId); for (int i = 0; i < patchFileRow.Fields.Length; i++) { string patchValue = patchFileRow[i] == null ? "" : patchFileRow[i].ToString(); string mainValue = mainFileRow[i] == null ? "" : mainFileRow[i].ToString(); if (1 == i) { // File.Component_ changes should not come from the shared file rows // that contain the file information as each individual transform might // have different changes (or no changes at all). } // File.Attributes should not changed for binary deltas else if (6 == i) { if (null != patchFileRow.Patch) { // File.Attribute should not change for binary deltas pairedFileRow.Attributes = mainFileRow.Attributes; mainFileRow.Fields[i].Modified = false; } } // File.Sequence is updated in pairedTransform, not mainTransform else if (7 == i) { // file sequence is updated in Patch table instead of File table for delta patches if (null != patchFileRow.Patch) { pairedFileRow.Fields[i].Modified = false; } else { pairedFileRow[i] = patchFileRow[i]; pairedFileRow.Fields[i].Modified = true; } mainFileRow.Fields[i].Modified = false; } else if (patchValue != mainValue) { mainFileRow[i] = patchFileRow[i]; mainFileRow.Fields[i].Modified = true; if (mainFileRow.Operation == RowOperation.None) { mainFileRow.Operation = RowOperation.Modify; } } } // copy MsiFileHash row for this File Row patchHashRow; if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out patchHashRow)) { patchHashRow = patchFileRow.Hash; } if (null != patchHashRow) { Table mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); Row mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); for (int i = 0; i < patchHashRow.Fields.Length; i++) { mainHashRow[i] = patchHashRow[i]; if (i > 1) { // assume all hash fields have been modified mainHashRow.Fields[i].Modified = true; } } // assume the MsiFileHash operation follows the File one mainHashRow.Operation = mainFileRow.Operation; } // copy MsiAssemblyName rows for this File List <Row> patchAssemblyNameRows = patchFileRow.AssemblyNames; if (null != patchAssemblyNameRows) { Table mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); foreach (Row patchAssemblyNameRow in patchAssemblyNameRows) { // Copy if there isn't an identical modified/added row already in the transform. bool foundMatchingModifiedRow = false; foreach (Row mainAssemblyNameRow in mainAssemblyNameTable.Rows) { if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) { foundMatchingModifiedRow = true; break; } } if (!foundMatchingModifiedRow) { Row mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); for (int i = 0; i < patchAssemblyNameRow.Fields.Length; i++) { mainAssemblyNameRow[i] = patchAssemblyNameRow[i]; } // assume value field has been modified mainAssemblyNameRow.Fields[2].Modified = true; mainAssemblyNameRow.Operation = mainFileRow.Operation; } } } // Add patch header for this file if (null != patchFileRow.Patch) { // Add the PatchFiles action automatically to the AdminExecuteSequence and InstallExecuteSequence tables. AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); // Add to Patch table Table patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); if (0 == patchTable.Rows.Count) { patchTable.Operation = TableOperation.Add; } Row patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); patchRow[0] = patchFileRow.File; patchRow[1] = patchFileRow.Sequence; FileInfo patchFile = new FileInfo(patchFileRow.Source); patchRow[2] = (int)patchFile.Length; patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; string streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; if (MsiInterop.MsiMaxStreamNameLength < streamName.Length) { streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); Table patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); if (0 == patchHeadersTable.Rows.Count) { patchHeadersTable.Operation = TableOperation.Add; } Row patchHeadersRow = patchHeadersTable.CreateRow(mainFileRow.SourceLineNumbers); patchHeadersRow[0] = streamName; patchHeadersRow[1] = patchFileRow.Patch; patchRow[5] = streamName; patchHeadersRow.Operation = RowOperation.Add; } else { patchRow[4] = patchFileRow.Patch; } patchRow.Operation = RowOperation.Add; } } else { // TODO: throw because all transform rows should have made it into the patch } } } } if (copyFromPatch) { this.Output.Tables.Remove("Media"); this.Output.Tables.Remove("File"); this.Output.Tables.Remove("MsiFileHash"); this.Output.Tables.Remove("MsiAssemblyName"); } } } finally { this.FileManagerCore.ActiveSubStorage = null; } #endif this.FileFacades = allFileRows; }
public void Execute() { List <FileTransfer> fileTransfers = new List <FileTransfer>(); Hashtable directories = new Hashtable(); RowDictionary <WixMediaRow> wixMediaRows = new RowDictionary <WixMediaRow>(this.WixMediaTable); using (Database db = new Database(this.DatabasePath, OpenDatabase.ReadOnly)) { using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`")) { while (true) { using (Record directoryRecord = directoryView.Fetch()) { if (null == directoryRecord) { break; } string sourceName = Installer.GetName(directoryRecord.GetString(3), true, this.LongNamesInImage); directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName)); } } } using (View fileView = db.OpenView("SELECT `Directory_`, `FileName` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_` AND `File`.`File`=?")) { using (Record fileQueryRecord = new Record(1)) { // for each file in the array of uncompressed files foreach (FileFacade facade in this.FileFacades) { MediaRow mediaRow = this.MediaRows.Get(facade.WixFile.DiskId); string relativeFileLayoutPath = null; WixMediaRow wixMediaRow = null; string mediaLayoutFolder = null; if (wixMediaRows.TryGetValue(mediaRow.GetKey(), out wixMediaRow)) { mediaLayoutFolder = wixMediaRow.Layout; } string mediaLayoutDirectory = this.ResolveMedia(mediaRow, mediaLayoutFolder, this.LayoutDirectory); // setup up the query record and find the appropriate file in the // previously executed file view fileQueryRecord[1] = facade.File.File; fileView.Execute(fileQueryRecord); using (Record fileRecord = fileView.Fetch()) { if (null == fileRecord) { throw new WixException(WixErrors.FileIdentifierNotFound(facade.File.SourceLineNumbers, facade.File.File)); } relativeFileLayoutPath = Binder.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], this.Compressed, this.LongNamesInImage); } // finally put together the base media layout path and the relative file layout path string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath); FileTransfer transfer; if (FileTransfer.TryCreate(facade.WixFile.Source, fileLayoutPath, false, "File", facade.File.SourceLineNumbers, out transfer)) { fileTransfers.Add(transfer); } } } } } this.FileTransfers = fileTransfers; }
public void Execute() { var fileRows = new RowDictionary <FileRow>(this.Output.Tables["File"]); var mediaRows = new RowDictionary <MediaRow>(this.Output.Tables["Media"]); // Calculate sequence numbers and media disk id layout for all file media information objects. if (OutputType.Module == this.Output.Type) { var lastSequence = 0; // Order by Component to group the files by directory. var optimized = this.OptimizedFileFacades(); foreach (var fileId in optimized.Select(f => f.File.File)) { var fileRow = fileRows.Get(fileId); fileRow.Sequence = ++lastSequence; } } else { int lastSequence = 0; MediaRow mediaRow = null; Dictionary <int, List <FileFacade> > patchGroups = new Dictionary <int, List <FileFacade> >(); // sequence the non-patch-added files var optimized = this.OptimizedFileFacades(); foreach (FileFacade facade in optimized) { if (null == mediaRow) { mediaRow = mediaRows.Get(facade.WixFile.DiskId); if (OutputType.Patch == this.Output.Type) { // patch Media cannot start at zero lastSequence = mediaRow.LastSequence; } } else if (mediaRow.DiskId != facade.WixFile.DiskId) { mediaRow.LastSequence = lastSequence; mediaRow = mediaRows.Get(facade.WixFile.DiskId); } if (0 < facade.WixFile.PatchGroup) { if (patchGroups.TryGetValue(facade.WixFile.PatchGroup, out var patchGroup)) { patchGroup = new List <FileFacade>(); patchGroups.Add(facade.WixFile.PatchGroup, patchGroup); } patchGroup.Add(facade); } else { var fileRow = fileRows.Get(facade.File.File); fileRow.Sequence = ++lastSequence; } } if (null != mediaRow) { mediaRow.LastSequence = lastSequence; mediaRow = null; } // sequence the patch-added files foreach (var patchGroup in patchGroups.Values) { foreach (var facade in patchGroup) { if (null == mediaRow) { mediaRow = mediaRows.Get(facade.WixFile.DiskId); } else if (mediaRow.DiskId != facade.WixFile.DiskId) { mediaRow.LastSequence = lastSequence; mediaRow = mediaRows.Get(facade.WixFile.DiskId); } var fileRow = fileRows.Get(facade.File.File); fileRow.Sequence = ++lastSequence; } } if (null != mediaRow) { mediaRow.LastSequence = lastSequence; } } }