public void FillFromPayloadRow(Output output, Row payloadRow) { SourceLineNumberCollection sourceLineNumbers = payloadRow.SourceLineNumbers; this[0] = payloadRow[0]; this[1] = payloadRow[1]; this[2] = (string)payloadRow[2] ?? String.Empty; this[3] = payloadRow[3]; this[4] = payloadRow[4]; this[5] = payloadRow[5] ?? String.Empty; this[6] = payloadRow[6]; // payload files sourced from a cabinet (think WixExtension with embedded binary wixlib) are considered "non-content files". ObjectField field = (ObjectField)payloadRow.Fields[2]; this.ContentFile = String.IsNullOrEmpty(field.CabinetFileId); ResolvePayloadInfo(this); return; }
/// <summary> /// Saves an output to a path on disk. /// </summary> /// <param name="path">Path to save output file to on disk.</param> /// <param name="binderExtension">If provided, the binder extension is used to bind files into the output.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> /// <param name="tempFilesLocation">Location for temporary files.</param> public void Save(string path, BinderExtension binderExtension, WixVariableResolver wixVariableResolver, string tempFilesLocation) { FileMode fileMode = FileMode.Create; // Check if there was a cab on the wixout when it was created if (null != this.cabPath) { // There was already a cab on the wixout when it was loaded. Reuse that one. File.Copy(this.cabPath, path, true); if (null != this.tempFileCollection) { this.tempFileCollection.Delete(); } fileMode = FileMode.Append; } else { Hashtable cabinets = new Hashtable(); StringCollection fileIds = new StringCollection(); StringCollection files = new StringCollection(); int index = 0; // resolve paths to files and create the output cabinet file foreach (Section section in this.sections) { foreach (Table table in section.Tables) { foreach (Row row in table.Rows) { foreach (Field field in row.Fields) { ObjectField objectField = field as ObjectField; if (null != objectField && null != objectField.Data) { string file = null; bool isDefault = true; // resolve localization and wix variables objectField.Data = wixVariableResolver.ResolveVariables(null, row.SourceLineNumbers, (string)objectField.Data, false, ref isDefault); // do not save the output if errors were found while resolving object paths if (wixVariableResolver.EncounteredError) { return; } // file is compressed in a cabinet (and not modified above) if (null != objectField.CabinetFileId && isDefault) { // index cabinets that have not been previously encountered if (!cabinets.ContainsKey(objectField.BaseUri)) { Uri baseUri = new Uri(objectField.BaseUri); string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath); string extractedDirectoryName = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension); // index the cabinet file's base URI (source location) and extracted directory cabinets.Add(objectField.BaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName)); } // set the path to the file once its extracted from the cabinet file = Path.Combine((string)cabinets[objectField.BaseUri], objectField.CabinetFileId); } else if (null != binderExtension) { file = binderExtension.ResolveFile((string)objectField.Data); } // add the file to the list of files to go in the cabinet if (null != file) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); objectField.CabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); files.Add(file); } } } } } } // extract files that come from cabinet files if (0 < cabinets.Count) { // ensure the temporary directory exists Directory.CreateDirectory(tempFilesLocation); foreach (DictionaryEntry cabinet in cabinets) { Uri baseUri = new Uri((string)cabinet.Key); string localPath; if ("embeddedresource" == baseUri.Scheme) { int bytesRead; byte[] buffer = new byte[512]; string originalLocalPath = Path.GetFullPath(baseUri.LocalPath.Substring(1)); string resourceName = baseUri.Fragment.Substring(1); Assembly assembly = Assembly.LoadFile(originalLocalPath); localPath = String.Concat(cabinet.Value, ".cab"); using (FileStream fs = File.OpenWrite(localPath)) { using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName)) { while (0 < (bytesRead = resourceStream.Read(buffer, 0, buffer.Length))) { fs.Write(buffer, 0, bytesRead); } } } } else // normal file { localPath = baseUri.LocalPath; } // extract the cabinet's files into a temporary directory Directory.CreateDirectory((string)cabinet.Value); using (WixExtractCab extractCab = new WixExtractCab()) { extractCab.Extract(localPath, (string)cabinet.Value); } } } // create the cabinet file if (0 < fileIds.Count) { try { using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(path), Path.GetDirectoryName(path), 0, 0, CompressionLevel.Mszip)) { for (int i = 0; i < fileIds.Count; i++) { cab.AddFile(files[i], fileIds[i]); } } } catch (FileNotFoundException e) { throw new WixException(WixErrors.FileNotFound(null, e.FileName)); } // append the output xml to the end of the newly created cabinet file fileMode = FileMode.Append; } } // Assure the location to output the xml exists Directory.CreateDirectory(Path.GetDirectoryName(path)); // save the xml using (FileStream fs = new FileStream(path, fileMode)) { XmlWriter writer = null; try { writer = new XmlTextWriter(fs, System.Text.Encoding.UTF8); writer.WriteStartDocument(); this.Persist(writer); writer.WriteEndDocument(); } finally { if (null != writer) { writer.Close(); } } } }
/// <summary> /// Saves a library to a path on disk. /// </summary> /// <param name="path">Path to save library file to on disk.</param> /// <param name="binderExtension">If provided, the binder extension is used to bind files into the library.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> public void Save(string path, BinderExtension binderExtension, WixVariableResolver wixVariableResolver) { FileMode fileMode = FileMode.Create; StringCollection fileIds = new StringCollection(); StringCollection files = new StringCollection(); int index = 0; // resolve paths to files and create the library cabinet file foreach (Section section in this.sections) { foreach (Table table in section.Tables) { foreach (Row row in table.Rows) { foreach (Field field in row.Fields) { ObjectField objectField = field as ObjectField; if (null != objectField) { if (null != binderExtension && null != objectField.Data) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); objectField.CabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); // resolve wix variables string resolvedValue = wixVariableResolver.ResolveVariables(null, row.SourceLineNumbers, (string)objectField.Data, false); files.Add(binderExtension.ResolveFile(resolvedValue)); } else // clear out a previous cabinet file id value { objectField.CabinetFileId = null; } } } } } } // do not save the library if errors were found while resolving object paths if (wixVariableResolver.EncounteredError) { return; } // create the cabinet file if (0 < fileIds.Count) { try { using (WixCreateCab cab = new WixCreateCab(Path.GetFileName(path), Path.GetDirectoryName(path), 0, 0, CompressionLevel.Mszip)) { for (int i = 0; i < fileIds.Count; i++) { cab.AddFile(files[i], fileIds[i]); } } } catch (FileNotFoundException e) { throw new WixException(WixErrors.FileNotFound(null, e.FileName)); } // append the library xml to the end of the newly created cabinet file fileMode = FileMode.Append; } // save the xml using (FileStream fs = new FileStream(path, fileMode)) { XmlWriter writer = null; try { writer = new XmlTextWriter(fs, System.Text.Encoding.UTF8); writer.WriteStartDocument(); this.Persist(writer); writer.WriteEndDocument(); } finally { if (null != writer) { writer.Close(); } } } }
/// <summary> /// Creates a transform by diffing two outputs. /// </summary> /// <param name="targetOutput">The target output.</param> /// <param name="updatedOutput">The updated output.</param> /// <returns>The transform.</returns> public Output Diff(Output targetOutput, Output updatedOutput) { Output transform = new Output(null); transform.Type = OutputType.Transform; transform.Codepage = updatedOutput.Codepage; string targetProductCode = null; string targetProductVersion = null; string targetUpgradeCode = null; string updatedProductCode = null; string updatedProductVersion = null; // compare the codepages if (targetOutput.Codepage != updatedOutput.Codepage) { this.OnMessage(WixErrors.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage)); if (null != updatedOutput.SourceLineNumbers) { this.OnMessage(WixErrors.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers)); } } // compare the output types if (targetOutput.Type != updatedOutput.Type) { throw new WixException(WixErrors.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString())); } // compare the contents of the tables foreach (Table targetTable in targetOutput.Tables) { Table updatedTable = updatedOutput.Tables[targetTable.Name]; // dropped tables if (null == updatedTable) { Table droppedTable = transform.Tables.EnsureTable(null, targetTable.Definition); droppedTable.Operation = TableOperation.Drop; } else // possibly modified tables { SortedList updatedPrimaryKeys = new SortedList(); SortedList targetPrimaryKeys = new SortedList(); // TODO compare the table definitions - they must be identical for the type, size, primary keys, etc... if (targetTable.Definition.Columns.Count != updatedTable.Definition.Columns.Count) { throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Different numbers of columns for {0}.", targetTable.Name)); } // index the target rows foreach (Row row in targetTable.Rows) { string primaryKey = row.GetPrimaryKey('/'); if (null != primaryKey) { targetPrimaryKeys.Add(primaryKey, row); } else // use the string representation of the row as its primary key (it may not be unique) { // this is provided for compatibility with unreal tables with no primary key // all real tables must specify at least one column as the primary key targetPrimaryKeys[row.ToString()] = row; } if ("Property" == targetTable.Name) { if ("ProductCode" == (string)row[0]) { targetProductCode = (string)row[1]; } else if ("ProductVersion" == (string)row[0]) { targetProductVersion = (string)row[1]; } else if ("UpgradeCode" == (string)row[0]) { targetUpgradeCode = (string)row[1]; } } } // index the updated rows foreach (Row row in updatedTable.Rows) { string primaryKey = row.GetPrimaryKey('/'); if (null != primaryKey) { updatedPrimaryKeys.Add(primaryKey, row); } else // use the string representation of the row as its primary key (it may not be unique) { // this is provided for compatibility with unreal tables with no primary key // all real tables must specify at least one column as the primary key updatedPrimaryKeys[row.ToString()] = row; } if ("Property" == targetTable.Name) { if ("ProductCode" == (string)row[0]) { updatedProductCode = (string)row[1]; } else if ("ProductVersion" == (string)row[0]) { updatedProductVersion = (string)row[1]; } } } // diff the target and updated rows foreach (DictionaryEntry targetPrimaryKeyEntry in targetPrimaryKeys) { string targetPrimaryKey = (string)targetPrimaryKeyEntry.Key; Row targetRow = (Row)targetPrimaryKeyEntry.Value; Row updatedRow = (Row)updatedPrimaryKeys[targetPrimaryKey]; if (null == updatedRow) // deleted row { Table modifiedTable = transform.EnsureTable(targetTable.Definition); targetRow.Operation = RowOperation.Delete; targetRow.SectionId = targetRow.SectionId + sectionDelimiter; modifiedTable.Rows.Add(targetRow); } else // possibly modified { updatedRow.Operation = RowOperation.None; if (!this.suppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) { Table table = transform.EnsureTable(updatedTable.Definition); updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; table.Rows.Add(updatedRow); } else { bool keepRow = false; if (this.preserveUnchangedRows) { keepRow = true; } for (int i = 0; i < updatedRow.Fields.Length; i++) { ColumnDefinition columnDefinition = updatedRow.Fields[i].Column; if (!columnDefinition.IsPrimaryKey) { bool modified = false; if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable) { if (null == targetRow[i] ^ null == updatedRow[i]) { modified = true; } else if (null != targetRow[i] && null != updatedRow[i]) { modified = ((int)targetRow[i] != (int)updatedRow[i]); } } else if (ColumnType.Object == columnDefinition.Type) { ObjectField targetObjectField = (ObjectField)targetRow.Fields[i]; ObjectField updatedObjectField = (ObjectField)updatedRow.Fields[i]; if (null != targetObjectField.CabinetFileId) { // TODO: handle this } if ((string)targetObjectField.Data != (string)updatedObjectField.Data) { updatedObjectField.PreviousData = (string)targetObjectField.Data; } // keep rows containing object fields so the files can be compared in the binder keepRow = !this.suppressKeepingSpecialRows; } else { modified = ((string)targetRow[i] != (string)updatedRow[i]); } if (modified) { updatedRow.Fields[i].Modified = true; updatedRow.Operation = RowOperation.Modify; keepRow = true; } } } if (keepRow) { Table modifiedTable = transform.EnsureTable(updatedTable.Definition); updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; modifiedTable.Rows.Add(updatedRow); } } } } // find the inserted rows foreach (DictionaryEntry updatedPrimaryKeyEntry in updatedPrimaryKeys) { string updatedPrimaryKey = (string)updatedPrimaryKeyEntry.Key; if (!targetPrimaryKeys.Contains(updatedPrimaryKey)) { Row updatedRow = (Row)updatedPrimaryKeyEntry.Value; Table modifiedTable = transform.EnsureTable(updatedTable.Definition); updatedRow.Operation = RowOperation.Add; updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; modifiedTable.Rows.Add(updatedRow); } } } } // added tables foreach (Table updatedTable in updatedOutput.Tables) { if (null == targetOutput.Tables[updatedTable.Name]) { Table addedTable = transform.Tables.EnsureTable(null, updatedTable.Definition); addedTable.Operation = TableOperation.Add; foreach (Row updatedRow in updatedTable.Rows) { updatedRow.Operation = RowOperation.Add; updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId; addedTable.Rows.Add(updatedRow); } } } // create the PID_REVNUMBER summary information property if (!this.suppressKeepingSpecialRows) { foreach (Row row in transform.Tables["_SummaryInformation"].Rows) { if (9 == (int)row[0]) { row[1] = String.Concat(targetProductCode, targetProductVersion, ';', updatedProductCode, updatedProductVersion, ';', targetUpgradeCode); } } } return(transform); }
/// <summary> /// Resolves paths to files. /// </summary> /// <param name="sectionTables">TableCollection of tables to process</param> /// <param name="binderFileManager">If provided, the binder file manager is used to bind files into the output.</param> /// <param name="wixVariableResolver">The Wix variable resolver.</param> /// <param name="tempFilesLocation">Location for temporary files.</param> /// <param name="cabinets">Hash of source cabinets.</param> /// <param name="fileIds">Collection of CabinetFileIds.</param> /// <param name="files">Collection of file paths from compressed files.</param> /// <param name="index">CabinetFileId generator.</param> public static void ResolveSectionFiles(TableCollection sectionTables, BinderFileManager binderFileManager, WixVariableResolver wixVariableResolver, string tempFilesLocation, Hashtable cabinets, StringCollection fileIds, StringCollection files, ref int index) { foreach (Table table in sectionTables) { foreach (Row row in table.Rows) { foreach (Field field in row.Fields) { ObjectField objectField = field as ObjectField; if (null != objectField && null != objectField.Data) { string file = null; string previousFile = null; bool isDefault = true; bool isPreviousDefault = true; // resolve localization and wix variables if there is a file manager that would use the value // if it was different, otherwise we just don't care so skip the whole variable resolution thing. if (null != wixVariableResolver && null != binderFileManager) { objectField.Data = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, (string)objectField.Data, false, ref isDefault); if (null != objectField.PreviousData) { objectField.PreviousData = wixVariableResolver.ResolveVariables(row.SourceLineNumbers, objectField.PreviousData, false, ref isPreviousDefault); } // do not save the output if errors were found while resolving object paths if (wixVariableResolver.EncounteredError) { return; } } // file is compressed in a cabinet (and not modified above) if (null != objectField.CabinetFileId && isDefault) { // index cabinets that have not been previously encountered if (!cabinets.ContainsKey(objectField.BaseUri)) { Uri baseUri = new Uri(objectField.BaseUri); string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath); string extractedDirectoryName = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension); // index the cabinet file's base URI (source location) and extracted directory cabinets.Add(objectField.BaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName)); } // set the path to the file once its extracted from the cabinet file = Path.Combine((string)cabinets[objectField.BaseUri], objectField.CabinetFileId); } else if (null != binderFileManager) { file = binderFileManager.ResolveFile((string)objectField.Data, table.Name, row.SourceLineNumbers, BindStage.Normal); } // add the file to the list of files to go in the cabinet if (null != file) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); objectField.CabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); files.Add(file); } // previous file is compressed in a cabinet (and not modified above) if (null != objectField.PreviousCabinetFileId && isPreviousDefault) { // index cabinets that have not been previously encountered if (!cabinets.ContainsKey(objectField.PreviousBaseUri)) { Uri baseUri = new Uri(objectField.PreviousBaseUri); string localFileNameWithoutExtension = Path.GetFileNameWithoutExtension(baseUri.LocalPath); string extractedDirectoryName = String.Format(CultureInfo.InvariantCulture, "cab_{0}_{1}", cabinets.Count, localFileNameWithoutExtension); // index the cabinet file's base URI (source location) and extracted directory cabinets.Add(objectField.PreviousBaseUri, Path.Combine(tempFilesLocation, extractedDirectoryName)); } // set the path to the file once its extracted from the cabinet previousFile = Path.Combine((string)cabinets[objectField.PreviousBaseUri], objectField.PreviousCabinetFileId); } else if (null != objectField.PreviousData && null != binderFileManager) { previousFile = binderFileManager.ResolveFile((string)objectField.PreviousData, table.Name, row.SourceLineNumbers, BindStage.Normal); } // add the file to the list of files to go in the cabinet if (null != previousFile) { string cabinetFileId = (index++).ToString(CultureInfo.InvariantCulture); objectField.PreviousCabinetFileId = cabinetFileId; fileIds.Add(cabinetFileId); files.Add(previousFile); } } } } } }
private Row CompareRows(Table targetTable, Row targetRow, Row updatedRow, out RowOperation operation, out bool keepRow) { Row comparedRow = null; keepRow = false; operation = RowOperation.None; if (null == targetRow ^ null == updatedRow) { if (null == targetRow) { operation = updatedRow.Operation = RowOperation.Add; comparedRow = updatedRow; } else if (null == updatedRow) { operation = targetRow.Operation = RowOperation.Delete; targetRow.SectionId = targetRow.SectionId + sectionDelimiter; comparedRow = targetRow; keepRow = true; } } else // possibly modified { updatedRow.Operation = RowOperation.None; if (!this.suppressKeepingSpecialRows && "_SummaryInformation" == targetTable.Name) { // ignore rows that shouldn't be in a transform if (Enum.IsDefined(typeof(SummaryInformation.Transform), (int)updatedRow[0])) { updatedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; comparedRow = updatedRow; keepRow = true; operation = RowOperation.Modify; } } else { if (this.preserveUnchangedRows) { keepRow = true; } for (int i = 0; i < updatedRow.Fields.Length; i++) { ColumnDefinition columnDefinition = updatedRow.Fields[i].Column; if (!columnDefinition.IsPrimaryKey) { bool modified = false; if (i >= targetRow.Fields.Length) { columnDefinition.Added = true; modified = true; } else if (ColumnType.Number == columnDefinition.Type && !columnDefinition.IsLocalizable) { if (null == targetRow[i] ^ null == updatedRow[i]) { modified = true; } else if (null != targetRow[i] && null != updatedRow[i]) { modified = ((int)targetRow[i] != (int)updatedRow[i]); } } else if (ColumnType.Preserved == columnDefinition.Type) { updatedRow.Fields[i].PreviousData = (string)targetRow.Fields[i].Data; // keep rows containing preserved fields so the historical data is available to the binder keepRow = !this.suppressKeepingSpecialRows; } else if (ColumnType.Object == columnDefinition.Type) { ObjectField targetObjectField = (ObjectField)targetRow.Fields[i]; ObjectField updatedObjectField = (ObjectField)updatedRow.Fields[i]; updatedObjectField.PreviousCabinetFileId = targetObjectField.CabinetFileId; updatedObjectField.PreviousBaseUri = targetObjectField.BaseUri; // always keep a copy of the previous data even if they are identical // This makes diff.wixmst clean and easier to control patch logic updatedObjectField.PreviousData = (string)targetObjectField.Data; // always remember the unresolved data for target build updatedObjectField.UnresolvedPreviousData = (string)targetObjectField.UnresolvedData; // keep rows containing object fields so the files can be compared in the binder keepRow = !this.suppressKeepingSpecialRows; } else { modified = ((string)targetRow[i] != (string)updatedRow[i]); } if (modified) { if (null != updatedRow.Fields[i].PreviousData) { updatedRow.Fields[i].PreviousData = targetRow.Fields[i].Data.ToString(); } updatedRow.Fields[i].Modified = true; operation = updatedRow.Operation = RowOperation.Modify; keepRow = true; } } } if (keepRow) { comparedRow = updatedRow; comparedRow.SectionId = targetRow.SectionId + sectionDelimiter + updatedRow.SectionId; } } } return(comparedRow); }