private static IList<SoftwareTag> CollectPackageTags(Output bundle) { List<SoftwareTag> tags = new List<SoftwareTag>(); Table packageTable = bundle.Tables["ChainPackageInfo"]; if (null != packageTable) { Table payloadTable = bundle.Tables["PayloadInfo"]; RowDictionary<PayloadInfoRow> payloads = new RowDictionary<PayloadInfoRow>(payloadTable); foreach (Row row in packageTable.Rows) { Compiler.ChainPackageType packageType = (Compiler.ChainPackageType)Enum.Parse(typeof(Compiler.ChainPackageType), (string)row[1], true); if (Compiler.ChainPackageType.Msi == packageType) { string packagePayloadId = (string)row[2]; PayloadInfoRow payload = (PayloadInfoRow)payloads[packagePayloadId]; using (Database db = new Database(payload.FullFileName)) { if (db.Tables.Contains("SoftwareIdentificationTag")) { using (View view = db.OpenView("SELECT `Regid`, `UniqueId`, `Type` FROM `SoftwareIdentificationTag`")) { view.Execute(); while (true) { using (Record record = view.Fetch()) { if (null == record) { break; } TagType type = String.IsNullOrEmpty(record.GetString(3)) ? TagType.Unknown : (TagType)Enum.Parse(typeof(TagType), record.GetString(3)); tags.Add(new SoftwareTag() { Regid = record.GetString(1), Id = record.GetString(2), Type = type }); } } } } } } } } return tags; }
public void WhenTryingToGetAValueAndTheKeyExistsShould() { //Arrange var key = 01; var expectedValue = "expectedValue"; var comparer = Comparer <int> .Default; var mockEqualityService = MockRepository.GenerateMock <IComparer <Cell <int, string> > >(); var mockEqualityServiceProvider = MockRepository.GenerateMock <IEqualityServiceProvider <int> >(); mockEqualityServiceProvider.Stub(x => x.GetKeyComparer(comparer)).Return(comparer); mockEqualityService .Stub(s => s.Compare( Arg <Cell <int, string> > .Matches(x => x.Key == key), Arg <Cell <int, string> > .Matches(x => x.Key == key))) .Return(0); var sut = new RowDictionary <int, string>(comparer, mockEqualityServiceProvider); sut.Add(key, expectedValue); string resultValue; //Act var resultBooleanValue = sut.TryGetValue(key, out resultValue); //Assert Assert.That(resultBooleanValue, Is.True); Assert.That(resultValue, Is.EqualTo(expectedValue)); }
public static void Main() { Spreadsheet.Spreadsheets = new SpreadsheetsDictionary(); var spreadsheetsDictionary = new SpreadsheetDictionary(); Spreadsheet.Spreadsheets.Add(0, spreadsheetsDictionary); var row = new RowDictionary(); spreadsheetsDictionary.Add(0, row); var cellA1 = new Cell(0, 0, 0); cellA1.Value = "250"; var cellB1 = new Cell(0, 0, 1); cellB1.Value = "250"; var cellC1 = new Cell(0, 0, 2); cellC1.Formula = "800 - SUM(A1:B1) + 100"; cellC1.HasFormula = true; row.Add(0, cellA1); row.Add(1, cellB1); row.Add(2, cellC1); //var spreadsheet = new Spreadsheet(); //spreadsheet.Calc(); var parsedCell = Spreadsheet.Spreadsheets[0][0][2]; var value = parsedCell.UpdateValue(); value = value; }
public void WhenTryingToGetAValueAndTheKeyDoesNotExistShould() { //Arrange var key = "key"; var thisKeyDoesNotExist = "thisKeyDoesNotExist"; var expectedValue = "expectedValue"; var comparer = (IComparer <string>)StringComparer.InvariantCultureIgnoreCase; var mockEqualityService = MockRepository.GenerateMock <IComparer <Cell <string, string> > >(); var mockEqualityServiceProvider = MockRepository.GenerateMock <IEqualityServiceProvider <string> >(); mockEqualityServiceProvider.Stub(x => x.GetKeyComparer(comparer)).Return(comparer); mockEqualityService .Stub(s => s.Compare( Arg <Cell <string, string> > .Matches(x => x.Key == key), Arg <Cell <string, string> > .Matches(x => x.Key == thisKeyDoesNotExist))) .Return(-1); var sut = new RowDictionary <string, string>(comparer, mockEqualityServiceProvider); sut.Add(key, expectedValue); string resultValue; //Act var resultBooleanValue = sut.TryGetValue(thisKeyDoesNotExist, out resultValue); //Assert Assert.That(resultBooleanValue, Is.False); }
/// <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; } }
public void ShouldBeAbleToSpecifyEqualityServiceToUse() { //Arrange var equalityService = StringComparer.CurrentCulture; //Act var sut = new RowDictionary <string, string>(equalityService); //Assert Assert.That(sut, Is.TypeOf <RowDictionary <string, string> >()); }
public static string ConfigurationStringGenerator() { string KiesString = ""; foreach (var item in ConfigurationList) { var MyKey = RowDictionary.First(x => x.Value.ParamName == item).Key; KiesString += MyKey; } ; return(KiesString); }
public void WhenInstantiatingANewDictionaryShouldCallGetComparer() { //Arrange var comparer = Comparer <int> .Default; var mockEqualityServiceProvider = MockRepository.GenerateMock <IEqualityServiceProvider <int> >(); //Act var sut = new RowDictionary <int, string>(comparer, mockEqualityServiceProvider); //Assert mockEqualityServiceProvider.AssertWasCalled(x => x.GetKeyComparer(comparer)); }
/// <summary> /// Initializes a new instance of the <see cref="Table"/> class. /// </summary> /// <param name="name">The name of the table.</param> /// <param name="db">The <see cref="Db"/> to which this <see cref="Table"/> is attached.</param> public Table(string name, Db db) { Util.ThrowIfStringParameterNullOrEmpty(name, "name"); Util.ThrowIfParameterNull(db, "db"); _rows = new RowDictionary(this); _name = name.ToLower(); _db = db; _xapiType = TypeCache.GetProxyType(name); _rows.Changed += delegate { OnChanged(EventArgs.Empty); }; }
/// <summary> /// Reorders Any WixSearch items. /// </summary> /// <param name="output">Output containing the tables to process.</param> private void ReorderWixSearch(Output output) { Table wixSearchTable = output.Tables["WixSearch"]; if (null == wixSearchTable || wixSearchTable.Rows.Count == 0) { // nothing to do! return; } RowDictionary rowDictionary = new RowDictionary(); foreach (Row row in wixSearchTable.Rows) { rowDictionary.AddRow(row); } Constraints constraints = new Constraints(); Table wixSearchRelationTable = output.Tables["WixSearchRelation"]; if (null != wixSearchRelationTable && wixSearchRelationTable.Rows.Count > 0) { // add relational info to our data... foreach (Row row in wixSearchRelationTable.Rows) { constraints.AddConstraint((string)row[0], (string)row[1]); } } this.FindCircularReference(constraints); if (this.Core.EncounteredError) { return; } this.FlattenDependentReferences(constraints); // Reorder by topographical sort (http://en.wikipedia.org/wiki/Topological_sorting) // We use a variation of Kahn (1962) algorithm as described in // Wikipedia, with the additional criteria that start nodes are sorted // lexicographically at each step to ensure a deterministic ordering // based on 'after' dependencies and ID. TopologicalSort sorter = new TopologicalSort(); List <string> sortedIds = sorter.Sort(rowDictionary.Keys, constraints); // Now, re-write the table with the searches in order... wixSearchTable.Rows.Clear(); foreach (string id in sortedIds) { wixSearchTable.Rows.Add(rowDictionary[id]); } }
public void ShouldThrowAnExceptionIfTheKeyDoesNotExist() { //Arrange var sut = new RowDictionary <string, string> { { "key01", "Value01" } }; string result; //Act //Assert Assert.That(() => result = sut["keyDoesNotExist"], Throws.TypeOf <KeyNotFoundException>()); }
private static IList <SoftwareTag> CollectPackageTags(Output bundle) { List <SoftwareTag> tags = new List <SoftwareTag>(); Table packageTable = bundle.Tables["ChainPackageInfo"]; if (null != packageTable) { Table payloadTable = bundle.Tables["PayloadInfo"]; RowDictionary <PayloadInfoRow> payloads = new RowDictionary <PayloadInfoRow>(payloadTable); foreach (Row row in packageTable.Rows) { Compiler.ChainPackageType packageType = (Compiler.ChainPackageType)Enum.Parse(typeof(Compiler.ChainPackageType), (string)row[1], true); if (Compiler.ChainPackageType.Msi == packageType) { string packagePayloadId = (string)row[2]; PayloadInfoRow payload = (PayloadInfoRow)payloads[packagePayloadId]; using (Database db = new Database(payload.FullFileName)) { if (db.Tables.Contains("SoftwareIdentificationTag")) { using (View view = db.OpenView("SELECT `Regid`, `UniqueId`, `Type` FROM `SoftwareIdentificationTag`")) { view.Execute(); while (true) { using (Record record = view.Fetch()) { if (null == record) { break; } TagType type = String.IsNullOrEmpty(record.GetString(3)) ? TagType.Unknown : (TagType)Enum.Parse(typeof(TagType), record.GetString(3)); tags.Add(new SoftwareTag() { Regid = record.GetString(1), Id = record.GetString(2), Type = type }); } } } } } } } } return(tags); }
public void ShouldBeAbleToRetrieveTheValueUsingTheKeyWhenThereIsMoreThanOneValueInserted() { //Arrange var key = 41; var anotherValue = "anotherValue"; var sut = new RowDictionary <int, string> { { 7, "abc" }, { 410, "era" }, { 8, "era" }, { key, anotherValue } }; //Act var result = sut[key]; //Assert Assert.That(result, Is.EqualTo(anotherValue)); }
public void ShouldSearchByKeyCaseInsensitiveLowerExample() { //Arrange var key = "keyUPPERandlower"; var expected = "expected"; var sut = new RowDictionary <string, string> { { key, expected } }; //Act var result03 = sut[key.ToLower()]; //Assert Assert.That(result03, Is.EqualTo(expected)); }
public void ShouldBeAbleToAddUsingBracket() { //Arrange var myKey = new { FirstName = "ABC", LastName = "DFG" }; var myValue = "myValue"; var sut = new RowDictionary <object, string>(); sut[myKey] = myValue; //Act var result = sut[myKey]; //Assert Assert.That(result, Is.EqualTo(myValue)); }
public void ShouldBeAbleToRetrieveTheValueUsingTheKeyWhenThereIsOnlyOneValueInserted() { //Arrange var key = 55; var expectedValue = "expectedValue"; var sut = new RowDictionary <int, string> { { key, expectedValue } }; //Act var result = sut[key]; //Assert Assert.That(result, Is.EqualTo(expectedValue)); }
public static RowDictionary RowsToDict(RowsRet rows) { RowDictionary rowDict = new RowDictionary(); List <string> .Enumerator headerRow = rows.Rows[0].Cols.GetEnumerator(); foreach (string cell in rows.Rows[1].Cols) { headerRow.MoveNext(); rowDict.Add(headerRow.Current, cell); } return(rowDict); }
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; }
public void ShouldBeAbleToRetrieveTheValueUsingTheKeyWhenTheKeyIsNonPrimitive() { //Arrange var myKey = new { FirstName = "ABC", LastName = "DFG" }; const string whenTheKeyIsAnObject = "WhenTheKeyIsAnObject"; var sut = new RowDictionary <object, string>(); sut.Add(myKey, whenTheKeyIsAnObject); string result; //Act sut.TryGetValue(myKey, out result); //Assert Assert.That(result, Is.EqualTo(whenTheKeyIsAnObject)); }
public void ShouldBeAbleToAddANewValueAndRetriveTheSameValueUsingTheSameKey() { //Arrange var key = "key"; var expectedValue = "Hola mundo"; var sut = new RowDictionary <string, string>(); sut.Add(key, expectedValue); string result; //Act sut.TryGetValue(key, out result); //Assert Assert.That(result, Is.EqualTo(expectedValue)); }
public void ShouldBeAbleToRetrieveTheValueUsingTheKeyWhenThereIsOnlyOneValueInserted() { //Arrange var key = 01; var expectedValue = "expectedValue"; var sut = new RowDictionary <int, string>(); sut.Add(key, expectedValue); string result; //Act sut.TryGetValue(key, out result); //Assert Assert.That(result, Is.EqualTo(expectedValue)); }
public void ShouldBeAbleToRetrieveTheValueUsingTheKeyWhenTheKeyIsNonPrimitive() { //Arrange var myKey = new { FirstName = "ABC", LastName = "DFG" }; var WhenTheKeyIsAnObject = "WhenTheKeyIsAnObject"; var sut = new RowDictionary <object, string> { { myKey, WhenTheKeyIsAnObject } }; //Act var result = sut[myKey]; //Assert Assert.That(result, Is.EqualTo(WhenTheKeyIsAnObject)); }
public void ShouldIterateExactlyAsManyTimesAsKeysAdded() { //Arrange var sut = new RowDictionary <int, string>(); sut.Add(01, "Value01"); sut.Add(02, "Value02"); sut.Add(03, "Value03"); sut.Add(04, "Value04"); sut.Add(05, "Value05"); sut.Add(06, "Value06"); //Act var totalItems = sut.Cast <object>().Count(); //Assert Assert.That(totalItems, Is.EqualTo(6)); }
public static RowDictionary RowsToDict(Row header, Row row) { RowDictionary rowDict = new RowDictionary(); List <string> .Enumerator headerRow = header.Cols.GetEnumerator(); foreach (string cell in row.Cols) { headerRow.MoveNext(); if (!rowDict.ContainsKey(headerRow.Current)) { rowDict.Add(headerRow.Current, cell); } } return(rowDict); }
public void WhenGettingAValueAndTheKeyExistsShould() { //Arrange var key = 01; var expectedValue = "expectedValue"; var comparer = Comparer <int> .Default; var mockEqualityServiceProvider = MockRepository.GenerateMock <IEqualityServiceProvider <int> >(); mockEqualityServiceProvider.Stub(x => x.GetKeyComparer(comparer)).Return(comparer); var sut = new RowDictionary <int, string>(comparer, mockEqualityServiceProvider); sut.Add(key, expectedValue); string resultValue; //Act resultValue = sut[key]; //Assert Assert.That(resultValue, Is.EqualTo(expectedValue)); }
//---------------------------------- public static string SentStringGenerator() { string ObjectString = ""; foreach (var item in ConfigurationList) { var MyValue = RowDictionary.First(x => x.Value.Name == item).Value.Value; ObjectString += MyValue.ToString(); if (ConfigurationList.IndexOf(item) == ConfigurationList.Count - 1 && VM.ParamConfigurationList.Count == 0) { ObjectString += ";"; } else { ObjectString += "|"; } } ; return(ObjectString); }
public void Execute() { if (null != this.WixBBControlTable) { RowDictionary <BBControlRow> bbControlRows = new RowDictionary <BBControlRow>(this.BBControlTable); foreach (Row wixRow in this.WixBBControlTable.Rows) { BBControlRow bbControlRow = bbControlRows.Get(wixRow.GetPrimaryKey()); bbControlRow.Text = this.ReadTextFile(bbControlRow.SourceLineNumbers, wixRow.FieldAsString(2)); } } if (null != this.WixControlTable) { RowDictionary <ControlRow> controlRows = new RowDictionary <ControlRow>(this.ControlTable); foreach (Row wixRow in this.WixControlTable.Rows) { ControlRow controlRow = controlRows.Get(wixRow.GetPrimaryKey()); controlRow.Text = this.ReadTextFile(controlRow.SourceLineNumbers, wixRow.FieldAsString(2)); } } }
public void Execute() { RowDictionary <WixBundleExePackageRow> exePackages = new RowDictionary <WixBundleExePackageRow>(this.ExePackageTable); RowDictionary <WixBundleMsiPackageRow> msiPackages = new RowDictionary <WixBundleMsiPackageRow>(this.MsiPackageTable); RowDictionary <WixBundleMspPackageRow> mspPackages = new RowDictionary <WixBundleMspPackageRow>(this.MspPackageTable); RowDictionary <WixBundleMsuPackageRow> msuPackages = new RowDictionary <WixBundleMsuPackageRow>(this.MsuPackageTable); Dictionary <string, PackageFacade> facades = new Dictionary <string, PackageFacade>(this.PackageTable.Rows.Count); foreach (WixBundlePackageRow package in this.PackageTable.Rows) { string id = package.WixChainItemId; PackageFacade facade = null; switch (package.Type) { case WixBundlePackageType.Exe: facade = new PackageFacade(package, exePackages.Get(id)); break; case WixBundlePackageType.Msi: facade = new PackageFacade(package, msiPackages.Get(id)); break; case WixBundlePackageType.Msp: facade = new PackageFacade(package, mspPackages.Get(id)); break; case WixBundlePackageType.Msu: facade = new PackageFacade(package, msuPackages.Get(id)); break; } facades.Add(id, facade); } this.PackageFacades = facades; }
public void Execute() { List <FileFacade> facades = new List <FileFacade>(this.FileTable.Rows.Count); RowDictionary <WixFileRow> wixFiles = new RowDictionary <WixFileRow>(this.WixFileTable); RowDictionary <WixDeltaPatchFileRow> deltaPatchFiles = new RowDictionary <WixDeltaPatchFileRow>(this.WixDeltaPatchFileTable); foreach (FileRow file in this.FileTable.Rows) { WixDeltaPatchFileRow deltaPatchFile = null; deltaPatchFiles.TryGetValue(file.File, out deltaPatchFile); facades.Add(new FileFacade(file, wixFiles[file.File], deltaPatchFile)); } if (null != this.WixDeltaPatchSymbolPathsTable) { this.ResolveDeltaPatchSymbolPaths(deltaPatchFiles, facades); } this.FileFacades = facades; }
private void AutomaticallySlipstreamPatches(Output bundle, ICollection<ChainPackageInfo> packages) { List<ChainPackageInfo> msiPackages = new List<ChainPackageInfo>(); Dictionary<string, List<WixBundlePatchTargetCodeRow>> targetsProductCode = new Dictionary<string, List<WixBundlePatchTargetCodeRow>>(); Dictionary<string, List<WixBundlePatchTargetCodeRow>> targetsUpgradeCode = new Dictionary<string, List<WixBundlePatchTargetCodeRow>>(); foreach (ChainPackageInfo package in packages) { if (Compiler.ChainPackageType.Msi == package.ChainPackageType) { // Keep track of all MSI packages. msiPackages.Add(package); } else if (Compiler.ChainPackageType.Msp == package.ChainPackageType && package.Slipstream) { // Index target ProductCodes and UpgradeCodes for slipstreamed MSPs. foreach (WixBundlePatchTargetCodeRow row in package.TargetCodes) { if (row.TargetsProductCode) { List<WixBundlePatchTargetCodeRow> rows; if (!targetsProductCode.TryGetValue(row.TargetCode, out rows)) { rows = new List<WixBundlePatchTargetCodeRow>(); targetsProductCode.Add(row.TargetCode, rows); } rows.Add(row); } else if (row.TargetsUpgradeCode) { List<WixBundlePatchTargetCodeRow> rows; if (!targetsUpgradeCode.TryGetValue(row.TargetCode, out rows)) { rows = new List<WixBundlePatchTargetCodeRow>(); targetsUpgradeCode.Add(row.TargetCode, rows); } } } } } Table slipstreamMspTable = bundle.EnsureTable(this.core.TableDefinitions["SlipstreamMsp"]); RowDictionary<Row> slipstreamMspRows = new RowDictionary<Row>(slipstreamMspTable); // Loop through the MSI and slipstream patches targeting it. foreach (ChainPackageInfo msi in msiPackages) { List<WixBundlePatchTargetCodeRow> rows; if (targetsProductCode.TryGetValue(msi.ProductCode, out rows)) { foreach (WixBundlePatchTargetCodeRow row in rows) { Row slipstreamMspRow = slipstreamMspTable.CreateRow(row.SourceLineNumbers, false); slipstreamMspRow[0] = msi.Id; slipstreamMspRow[1] = row.MspPackageId; if (slipstreamMspRows.TryAdd(slipstreamMspRow)) { slipstreamMspTable.Rows.Add(slipstreamMspRow); } } rows = null; } if (!String.IsNullOrEmpty(msi.UpgradeCode) && targetsUpgradeCode.TryGetValue(msi.UpgradeCode, out rows)) { foreach (WixBundlePatchTargetCodeRow row in rows) { Row slipstreamMspRow = slipstreamMspTable.CreateRow(row.SourceLineNumbers, false); slipstreamMspRow[0] = msi.Id; slipstreamMspRow[1] = row.MspPackageId; if (slipstreamMspRows.TryAdd(slipstreamMspRow)) { slipstreamMspTable.Rows.Add(slipstreamMspRow); } } rows = null; } } }
/// <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); } }
/// <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; }
public void Execute() { Debug.Assert(OutputType.Patch != this.Output.Type); var allFileRows = this.CopyOutFileRows ? new List <FileFacade>() : null; var copyToPatch = (allFileRows != null); var copyFromPatch = !copyToPatch; var patchMediaRows = new RowDictionary <MediaRow>(); var patchMediaFileRows = new Dictionary <int, RowDictionary <WixFileRow> >(); var patchActualFileTable = this.Output.EnsureTable(this.TableDefinitions["File"]); var 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; if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } mediaFileRows.Add(patchFileRow); } var patchMediaTable = this.Output.EnsureTable(this.TableDefinitions["Media"]); patchMediaRows = new RowDictionary <MediaRow>(patchMediaTable); } // Index paired transforms by name without the "#" prefix. var pairedTransforms = this.Output.SubStorages.Where(s => s.Name.StartsWith("#")).ToDictionary(s => s.Name.Substring(1), s => s.Data); //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 (var substorage in this.Output.SubStorages) { if (substorage.Name.StartsWith("#")) { // no changes necessary for paired transforms continue; } var mainTransform = substorage.Data; var mainWixFileTable = mainTransform.Tables["WixFile"]; var mainMsiFileHashTable = mainTransform.Tables["MsiFileHash"]; this.FileManagerCore.ActiveSubStorage = substorage; var mainWixFiles = new RowDictionary <WixFileRow>(mainWixFileTable); var mainMsiFileHashIndex = new RowDictionary <Row>(); var mainFileTable = mainTransform.Tables["File"]; var pairedTransform = pairedTransforms[substorage.Name]; // copy Media.LastSequence and index the MsiFileHash table if it exists. if (copyFromPatch) { var pairedMediaTable = pairedTransform.Tables["Media"]; foreach (MediaRow pairedMediaRow in pairedMediaTable.Rows) { var 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 var pairedFileTable = pairedTransform.Tables["File"]; var 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; } var mainWixFileRow = mainWixFiles.Get(mainFileRow.File); if (copyToPatch) // when copying to the patch, we need compare the underlying files and include all file changes. { var objectField = (ObjectField)mainWixFileRow.Fields[6]; var 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 |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~WindowsInstallerConstants.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 (var field in mainFileRow.Fields) { field.Modified = false; } if (null != pairedFileRow) { pairedFileRow.Attributes &= ~WindowsInstallerConstants.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 |= WindowsInstallerConstants.MsidbFileAttributesPatchAdded; pairedFileRow.Attributes &= ~WindowsInstallerConstants.MsidbFileAttributesNoncompressed; pairedFileRow.Fields[6].Modified = true; pairedFileRow.Operation = RowOperation.Add; } } // index patch files by diskId+fileId int diskId = mainWixFileRow.DiskId; if (!patchMediaFileRows.TryGetValue(diskId, out var mediaFileRows)) { mediaFileRows = new RowDictionary <WixFileRow>(); patchMediaFileRows.Add(diskId, mediaFileRows); } var fileId = mainFileRow.File; var patchFileRow = mediaFileRows.Get(fileId); if (copyToPatch) { if (null == patchFileRow) { var 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)) { this.Messaging.Write(ErrorMessages.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) { var pairedFileRow = pairedFileRows.Get(fileId); for (var i = 0; i < patchFileRow.Fields.Length; i++) { var patchValue = patchFileRow[i] == null ? String.Empty : patchFileRow.FieldAsString(i); var mainValue = mainFileRow[i] == null ? String.Empty : mainFileRow.FieldAsString(i); 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 if (!mainMsiFileHashIndex.TryGetValue(patchFileRow.File, out var patchHashRow)) { patchHashRow = patchFileRow.Hash; } if (null != patchHashRow) { var mainHashTable = mainTransform.EnsureTable(this.TableDefinitions["MsiFileHash"]); var mainHashRow = mainHashTable.CreateRow(mainFileRow.SourceLineNumbers); for (var 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) { var mainAssemblyNameTable = mainTransform.EnsureTable(this.TableDefinitions["MsiAssemblyName"]); foreach (var patchAssemblyNameRow in patchAssemblyNameRows) { // Copy if there isn't an identical modified/added row already in the transform. var foundMatchingModifiedRow = false; foreach (var mainAssemblyNameRow in mainAssemblyNameTable.Rows) { if (RowOperation.None != mainAssemblyNameRow.Operation && mainAssemblyNameRow.GetPrimaryKey('/').Equals(patchAssemblyNameRow.GetPrimaryKey('/'))) { foundMatchingModifiedRow = true; break; } } if (!foundMatchingModifiedRow) { var mainAssemblyNameRow = mainAssemblyNameTable.CreateRow(mainFileRow.SourceLineNumbers); for (var 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. this.AddPatchFilesActionToSequenceTable(SequenceTable.AdminExecuteSequence, mainTransform, pairedTransform, mainFileRow); this.AddPatchFilesActionToSequenceTable(SequenceTable.InstallExecuteSequence, mainTransform, pairedTransform, mainFileRow); // Add to Patch table var patchTable = pairedTransform.EnsureTable(this.TableDefinitions["Patch"]); if (0 == patchTable.Rows.Count) { patchTable.Operation = TableOperation.Add; } var patchRow = patchTable.CreateRow(mainFileRow.SourceLineNumbers); patchRow[0] = patchFileRow.File; patchRow[1] = patchFileRow.Sequence; var patchFile = new FileInfo(patchFileRow.Source); patchRow[2] = (int)patchFile.Length; patchRow[3] = 0 == (PatchAttributeType.AllowIgnoreOnError & patchFileRow.PatchAttributes) ? 0 : 1; var streamName = patchTable.Name + "." + patchRow[0] + "." + patchRow[1]; if (Msi.MsiInterop.MsiMaxStreamNameLength < streamName.Length) { streamName = "_" + Guid.NewGuid().ToString("D").ToUpperInvariant().Replace('-', '_'); var patchHeadersTable = pairedTransform.EnsureTable(this.TableDefinitions["MsiPatchHeaders"]); if (0 == patchHeadersTable.Rows.Count) { patchHeadersTable.Operation = TableOperation.Add; } var 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; } this.FileFacades = allFileRows; }