/// <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 this.Messaging.Write(VerboseMessages.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(ErrorMessages.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(ErrorMessages.SplitCabinetNameCollision(newCabinetName, firstCabinetName)); } } // Check if the last Split Cabinet was found in the Media Table if (!lastSplitCabinetFound) { throw new WixException(ErrorMessages.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(); } }