internal static MsiPropertyInfo[] GetPropertiesFromDatabase(Database msidb) { int[] standardIDs = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19}; ArrayList properties = new ArrayList(); using (SummaryInformation summaryInfo = new SummaryInformation(msidb)) { foreach (int propID in standardIDs) { bool failed = false; object propValue = null; try { propValue = summaryInfo.GetProperty(propID); } catch { failed = true; } if (!failed) properties.Add(new MsiPropertyInfo(propID, propValue)); } } return (MsiPropertyInfo[]) properties.ToArray(typeof (MsiPropertyInfo)); }
public void ApplyTransform(string transformFile) { // get the curret validation bits TransformErrorConditions conditions = TransformErrorConditions.None; using (SummaryInformation summaryInfo = new SummaryInformation(transformFile)) { string value = summaryInfo.GetProperty((int)SummaryInformation.Transform.ValidationFlags); try { int validationFlags = Int32.Parse(value, CultureInfo.InvariantCulture); conditions = (TransformErrorConditions)(validationFlags & 0xffff); } catch (FormatException) { // fallback to default of None } } this.ApplyTransform(transformFile, conditions); }
/// <summary> /// Retrieve files and their information from merge modules. /// </summary> /// <param name="output">Internal representation of the msi database to operate upon.</param> /// <param name="fileRows">The indexed file rows.</param> private void ProcessMergeModules(Output output, FileRowCollection fileRows) { Table wixMergeTable = output.Tables["WixMerge"]; if (null != wixMergeTable) { IMsmMerge2 merge = NativeMethods.GetMsmMerge(); // Get the output's minimum installer version int outputInstallerVersion = int.MinValue; Table summaryInformationTable = output.Tables["_SummaryInformation"]; if (null != summaryInformationTable) { foreach (Row row in summaryInformationTable.Rows) { if (14 == (int)row[0]) { outputInstallerVersion = Convert.ToInt32(row[1], CultureInfo.InvariantCulture); break; } } } foreach (Row row in wixMergeTable.Rows) { bool containsFiles = false; WixMergeRow wixMergeRow = (WixMergeRow)row; try { // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) { if (db.TableExists("File") && db.TableExists("Component")) { Hashtable uniqueModuleFileIdentifiers = System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable(); using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`")) { // add each file row from the merge module into the file row collection (check for errors along the way) while (true) { using (Record record = view.Fetch()) { if (null == record) { break; } // NOTE: this is very tricky - the merge module file rows are not added to the // file table because they should not be created via idt import. Instead, these // rows are created by merging in the actual modules FileRow fileRow = new FileRow(null, this.core.TableDefinitions["File"]); fileRow.File = record[1]; fileRow.Compressed = wixMergeRow.FileCompression; fileRow.Directory = record[2]; fileRow.DiskId = wixMergeRow.DiskId; fileRow.FromModule = true; fileRow.PatchGroup = -1; fileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat), Path.DirectorySeparatorChar, record[1]); FileRow collidingFileRow = fileRows[fileRow.File]; FileRow collidingModuleFileRow = (FileRow)uniqueModuleFileIdentifiers[fileRow.File]; if (null == collidingFileRow && null == collidingModuleFileRow) { fileRows.Add(fileRow); // keep track of file identifiers in this merge module uniqueModuleFileIdentifiers.Add(fileRow.File, fileRow); } else // collision(s) detected { // case-sensitive collision with another merge module or a user-authored file identifier if (null != collidingFileRow) { this.core.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFileRow.File)); } // case-insensitive collision with another file identifier in the same merge module if (null != collidingModuleFileRow) { this.core.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, fileRow.File, collidingModuleFileRow.File)); } } containsFiles = true; } } } } // Get the summary information to detect the Schema using (SummaryInformation summaryInformation = new SummaryInformation(db)) { string moduleInstallerVersionString = summaryInformation.GetProperty(14); try { int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); if (moduleInstallerVersion > outputInstallerVersion) { this.core.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, outputInstallerVersion)); } } catch (FormatException) { throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile, moduleInstallerVersionString)); } } } } catch (FileNotFoundException) { throw new WixException(WixErrors.FileNotFound(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); } catch (Win32Exception) { throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile)); } // if the module has files and creating layout if (containsFiles && !this.suppressLayout) { bool moduleOpen = false; short mergeLanguage; try { mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture); } catch (System.FormatException) { this.core.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language)); continue; } try { merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage); moduleOpen = true; string safeMergeId = wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat); // extract the module cabinet, then explode all of the files to a temp directory string moduleCabPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, safeMergeId, ".module.cab"); merge.ExtractCAB(moduleCabPath); string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId); Directory.CreateDirectory(mergeIdPath); using (WixExtractCab extractCab = new WixExtractCab()) { try { extractCab.Extract(moduleCabPath, mergeIdPath); } catch (FileNotFoundException) { throw new WixException(WixErrors.CabFileDoesNotExist(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath)); } catch { throw new WixException(WixErrors.CabExtractionFailed(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath)); } } } catch (COMException ce) { throw new WixException(WixErrors.UnableToOpenModule(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile, ce.Message)); } finally { if (moduleOpen) { merge.CloseModule(); } } } } } }
/// <summary> /// Reads the SummaryInformation and creates the Package element. /// </summary> private void ProcessSummaryInformation() { using (SummaryInformation summary = new SummaryInformation(this.inputDatabase)) { this.core.Writer.WriteStartElement("Package"); this.core.OnMessage(WixVerboses.ProcessingSummaryInformation(null, VerboseLevel.Verbose)); this.core.WriteAttributeString("Id", this.StripBraces(summary.GetProperty(9).ToString())); // PackageCode this.core.WriteAttributeString("Keywords", summary.GetProperty(5).ToString()); // package keywords this.core.WriteAttributeString("Description", summary.GetProperty(3).ToString()); // description this.core.WriteAttributeString("Comments", summary.GetProperty(6).ToString().Trim()); // package comments this.core.WriteAttributeString("Manufacturer", summary.GetProperty(4).ToString()); // manufacturer this.core.WriteAttributeString("InstallerVersion", summary.GetProperty(14).ToString()); // minimum required installer version string[] platformAndLanguage = summary.GetProperty(7).ToString().Split(';'); if (0 < platformAndLanguage.Length) { this.core.WriteAttributeString("Platforms", platformAndLanguage[0]); } if (1 < platformAndLanguage.Length) { this.core.WriteAttributeString("Languages", platformAndLanguage[1]); } uint sourceFlags = Convert.ToUInt16(summary.GetProperty(15)); if (0 < (sourceFlags & 1)) { this.core.WriteAttributeString("ShortNames", "yes"); } if (0 < (sourceFlags & 2)) { this.core.WriteAttributeString("Compressed", "yes"); this.summaryInfoCompressed = true; } if (0 < (sourceFlags & 4)) { this.core.WriteAttributeString("AdminImage", "yes"); } if (0 < (sourceFlags & 8)) { this.core.WriteAttributeString("InstallPrivileges", "limited"); } this.core.WriteAttributeString("SummaryCodepage", summary.GetProperty(1).ToString()); int security = Convert.ToInt32(summary.GetProperty(19), CultureInfo.InvariantCulture); switch (security) { case 0: this.core.WriteAttributeString("ReadOnly", "no"); break; case 2: // nothing to write; this is the default value break; case 4: this.core.WriteAttributeString("ReadOnly", "yes"); break; default: // let the compiler use the default value break; } this.core.Writer.WriteEndElement(); summary.Close(false); } }
/// <summary> /// Unbind an MSI database file. /// </summary> /// <param name="databaseFile">The database file.</param> /// <param name="database">The opened database.</param> /// <param name="outputType">The type of output to create.</param> /// <param name="exportBasePath">The path where files should be exported.</param> /// <param name="skipSummaryInfo">Option to skip unbinding the _SummaryInformation table.</param> /// <returns>The output representing the database.</returns> private Output UnbindDatabase(string databaseFile, Database database, OutputType outputType, string exportBasePath, bool skipSummaryInfo) { string modularizationGuid = null; Output output = new Output(SourceLineNumberCollection.FromFileName(databaseFile)); View validationView = null; // set the output type output.Type = outputType; // get the codepage database.Export("_ForceCodepage", this.TempFilesLocation, "_ForceCodepage.idt"); using (StreamReader sr = File.OpenText(Path.Combine(this.TempFilesLocation, "_ForceCodepage.idt"))) { string line; while (null != (line = sr.ReadLine())) { string[] data = line.Split('\t'); if (2 == data.Length) { output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture); } } } // get the summary information table if it exists; it won't if unbinding a transform if (!skipSummaryInfo) { using (SummaryInformation summaryInformation = new SummaryInformation(database)) { Table table = new Table(null, this.tableDefinitions["_SummaryInformation"]); for (int i = 1; 19 >= i; i++) { string value = summaryInformation.GetProperty(i); if (0 < value.Length) { Row row = table.CreateRow(output.SourceLineNumbers); row[0] = i; row[1] = value; } } output.Tables.Add(table); } } try { // open a view on the validation table if it exists if (database.TableExists("_Validation")) { validationView = database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?"); } // get the normal tables using (View tablesView = database.OpenExecuteView("SELECT * FROM _Tables")) { while (true) { using (Record tableRecord = tablesView.Fetch()) { if (null == tableRecord) { break; } string tableName = tableRecord.GetString(1); using (View tableView = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) { TableDefinition tableDefinition = new TableDefinition(tableName, false, false); Hashtable tablePrimaryKeys = new Hashtable(); using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES), columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES)) { int columnCount = columnNameRecord.GetFieldCount(); // index the primary keys using (Record primaryKeysRecord = database.PrimaryKeys(tableName)) { int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount(); for (int i = 1; i <= primaryKeysFieldCount; i++) { tablePrimaryKeys[primaryKeysRecord.GetString(i)] = null; } } for (int i = 1; i <= columnCount; i++) { string columnName = columnNameRecord.GetString(i); string idtType = columnTypeRecord.GetString(i); ColumnType columnType; int length; bool nullable; ColumnCategory columnCategory = ColumnCategory.Unknown; ColumnModularizeType columnModularizeType = ColumnModularizeType.None; bool primary = tablePrimaryKeys.Contains(columnName); bool minValueSet = false; int minValue = -1; bool maxValueSet = false; int maxValue = -1; string keyTable = null; bool keyColumnSet = false; int keyColumn = -1; string category = null; string set = null; string description = null; // get the column type, length, and whether its nullable switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture)) { case 'i': columnType = ColumnType.Number; break; case 'l': columnType = ColumnType.Localized; break; case 's': columnType = ColumnType.String; break; case 'v': columnType = ColumnType.Object; break; default: // TODO: error columnType = ColumnType.Unknown; break; } length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture); nullable = Char.IsUpper(idtType[0]); // try to get validation information if (null != validationView) { using (Record validationRecord = new Record(2)) { validationRecord.SetString(1, tableName); validationRecord.SetString(2, columnName); validationView.Execute(validationRecord); } using (Record validationRecord = validationView.Fetch()) { if (null != validationRecord) { string validationNullable = validationRecord.GetString(3); minValueSet = !validationRecord.IsNull(4); minValue = (minValueSet ? validationRecord.GetInteger(4) : -1); maxValueSet = !validationRecord.IsNull(5); maxValue = (maxValueSet ? validationRecord.GetInteger(5) : -1); keyTable = (!validationRecord.IsNull(6) ? validationRecord.GetString(6) : null); keyColumnSet = !validationRecord.IsNull(7); keyColumn = (keyColumnSet ? validationRecord.GetInteger(7) : -1); category = (!validationRecord.IsNull(8) ? validationRecord.GetString(8) : null); set = (!validationRecord.IsNull(9) ? validationRecord.GetString(9) : null); description = (!validationRecord.IsNull(10) ? validationRecord.GetString(10) : null); // check the validation nullable value against the column definition if (null == validationNullable) { // TODO: warn for illegal validation nullable column } else if ((nullable && "Y" != validationNullable) || (!nullable && "N" != validationNullable)) { // TODO: warn for mismatch between column definition and validation nullable } // convert category to ColumnCategory if (null != category) { try { columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true); } catch (ArgumentException) { columnCategory = ColumnCategory.Unknown; } } } else { // TODO: warn about no validation information } } } // guess the modularization type if ("Icon" == keyTable && 1 == keyColumn) { columnModularizeType = ColumnModularizeType.Icon; } else if ("Condition" == columnName) { columnModularizeType = ColumnModularizeType.Condition; } else if (ColumnCategory.Formatted == columnCategory || ColumnCategory.FormattedSDDLText == columnCategory) { columnModularizeType = ColumnModularizeType.Property; } else if (ColumnCategory.Identifier == columnCategory) { columnModularizeType = ColumnModularizeType.Column; } tableDefinition.Columns.Add(new ColumnDefinition(columnName, columnType, length, primary, nullable, columnModularizeType, (ColumnType.Localized == columnType), minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, columnCategory, set, description, true, true)); } } // use our table definitions if core properties are the same; this allows us to take advantage // of wix concepts like localizable columns which current code assumes if (this.tableDefinitions.Contains(tableName) && 0 == tableDefinition.CompareTo(this.tableDefinitions[tableName])) { tableDefinition = this.tableDefinitions[tableName]; } Table table = new Table(null, tableDefinition); while (true) { using (Record rowRecord = tableView.Fetch()) { if (null == rowRecord) { break; } int recordCount = rowRecord.GetFieldCount(); Row row = table.CreateRow(output.SourceLineNumbers); for (int i = 0; recordCount > i && row.Fields.Length > i; i++) { if (rowRecord.IsNull(i + 1)) { if (!row.Fields[i].Column.IsNullable) { // TODO: display an error for a null value in a non-nullable field OR // display a warning and put an empty string in the value to let the compiler handle it // (the second option is risky because the later code may make certain assumptions about // the contents of a row value) } } else { switch (row.Fields[i].Column.Type) { case ColumnType.Number: bool success = false; int intValue = rowRecord.GetInteger(i + 1); if (row.Fields[i].Column.IsLocalizable) { success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture)); } else { success = row.BestEffortSetField(i, intValue); } if (!success) { this.OnMessage(WixWarnings.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name)); } break; case ColumnType.Object: string sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES"; if (null != exportBasePath) { string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.')); sourceFile = Path.Combine(exportBasePath, relativeSourceFile); // ensure the parent directory exists System.IO.Directory.CreateDirectory(Path.Combine(exportBasePath, tableName)); using (FileStream fs = System.IO.File.Create(sourceFile)) { int bytesRead; byte[] buffer = new byte[512]; while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length))) { fs.Write(buffer, 0, bytesRead); } } } row[i] = sourceFile; break; default: string value = rowRecord.GetString(i + 1); switch (row.Fields[i].Column.Category) { case ColumnCategory.Guid: value = value.ToUpper(CultureInfo.InvariantCulture); break; } // de-modularize if (!this.suppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType) { Regex modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}"); if (null == modularizationGuid) { Match match = modularization.Match(value); if (match.Success) { modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}'); } } value = modularization.Replace(value, String.Empty); } // escape "$(" for the preprocessor value = value.Replace("$(", "$$("); // escape things that look like wix variables MatchCollection matches = Common.WixVariableRegex.Matches(value); for (int j = matches.Count - 1; 0 <= j; j--) { value = value.Insert(matches[j].Index, "!"); } row[i] = value; break; } } } } } output.Tables.Add(table); } } } } } finally { if (null != validationView) { validationView.Close(); } } // set the modularization guid as the PackageCode if (null != modularizationGuid) { Table table = output.Tables["_SummaryInformation"]; foreach (Row row in table.Rows) { if (9 == (int)row[0]) // PID_REVNUMBER { row[1] = modularizationGuid; } } } if (this.isAdminImage) { GenerateWixFileTable(databaseFile, output); GenerateSectionIds(output); } return output; }
/// <summary> /// Unbind an MSI transform file. /// </summary> /// <param name="transformFile">The transform file.</param> /// <param name="exportBasePath">The path where files should be exported.</param> /// <returns>The unbound transform.</returns> private Output UnbindTransform(string transformFile, string exportBasePath) { Output transform = new Output(SourceLineNumberCollection.FromFileName(transformFile)); transform.Type = OutputType.Transform; // get the summary information table using (SummaryInformation summaryInformation = new SummaryInformation(transformFile)) { Table table = transform.Tables.EnsureTable(null, this.tableDefinitions["_SummaryInformation"]); for (int i = 1; 19 >= i; i++) { string value = summaryInformation.GetProperty(i); if (0 < value.Length) { Row row = table.CreateRow(transform.SourceLineNumbers); row[0] = i; row[1] = value; } } } // create a schema msi which hopefully matches the table schemas in the transform Output schemaOutput = new Output(null); string msiDatabaseFile = Path.Combine(this.tempFiles.BasePath, "schema.msi"); foreach (TableDefinition tableDefinition in this.tableDefinitions) { // skip unreal tables and the Patch table if (!tableDefinition.IsUnreal && "Patch" != tableDefinition.Name) { schemaOutput.EnsureTable(tableDefinition); } } Hashtable addedRows = new Hashtable(); Table transformViewTable; // bind the schema msi using (Binder binder = new Binder()) { binder.SuppressAddingValidationRows = true; binder.WixVariableResolver = new WixVariableResolver(); binder.GenerateDatabase(schemaOutput, msiDatabaseFile, true, false); // apply the transform to the database and retrieve the modifications using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { // apply the transform with the ViewTransform option to collect all the modifications msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); // unbind the database Output transformViewOutput = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath, true); // index the added and possibly modified rows (added rows may also appears as modified rows) transformViewTable = transformViewOutput.Tables["_TransformView"]; Hashtable modifiedRows = new Hashtable(); foreach (Row row in transformViewTable.Rows) { string tableName = (string) row[0]; string columnName = (string) row[1]; string primaryKeys = (string) row[2]; if ("INSERT" == columnName) { string index = String.Concat(tableName, ':', primaryKeys); addedRows.Add(index, null); } else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row { string index = String.Concat(tableName, ':', primaryKeys); modifiedRows[index] = row; } } // create placeholder rows for modified rows to make the transform insert the updated values when its applied foreach (Row row in modifiedRows.Values) { string tableName = (string) row[0]; string columnName = (string) row[1]; string primaryKeys = (string) row[2]; string index = String.Concat(tableName, ':', primaryKeys); // ignore information for added rows if (!addedRows.Contains(index)) { Table table = schemaOutput.Tables[tableName]; this.CreateRow(table, primaryKeys, true); } } } // re-bind the schema output with the placeholder rows binder.GenerateDatabase(schemaOutput, msiDatabaseFile, true, false); } // apply the transform to the database and retrieve the modifications using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { try { // apply the transform msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All); // commit the database to guard against weird errors with streams msiDatabase.Commit(); } catch (Win32Exception ex) { if (0x65B == ex.NativeErrorCode) { // this commonly happens when the transform was built // against a database schema different from the internal // table definitions throw new WixException(WixErrors.TransformSchemaMismatch()); } } // unbind the database Output output = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath, true); // index all the rows to easily find modified rows Hashtable rows = new Hashtable(); foreach (Table table in output.Tables) { foreach (Row row in table.Rows) { rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); } } // process the _TransformView rows into transform rows foreach (Row row in transformViewTable.Rows) { string tableName = (string)row[0]; string columnName = (string)row[1]; string primaryKeys = (string)row[2]; Table table = transform.Tables.EnsureTable(null, this.tableDefinitions[tableName]); if ("CREATE" == columnName) // added table { table.Operation = TableOperation.Add; } else if ("DELETE" == columnName) // deleted row { Row deletedRow = this.CreateRow(table, primaryKeys, false); deletedRow.Operation = RowOperation.Delete; } else if ("DROP" == columnName) // dropped table { table.Operation = TableOperation.Drop; } else if ("INSERT" == columnName) // added row { string index = String.Concat(tableName, ':', primaryKeys); Row addedRow = (Row)rows[index]; addedRow.Operation = RowOperation.Add; table.Rows.Add(addedRow); } else if (null != primaryKeys) // modified row { string index = String.Concat(tableName, ':', primaryKeys); // the _TransformView table includes information for added rows // that looks like modified rows so it sometimes needs to be ignored if (!addedRows.Contains(index)) { Row modifiedRow = (Row)rows[index]; // mark the field as modified int indexOfModifiedValue = modifiedRow.TableDefinition.Columns.IndexOf(columnName); modifiedRow.Fields[indexOfModifiedValue].Modified = true; // move the modified row into the transform the first time its encountered if (RowOperation.None == modifiedRow.Operation) { modifiedRow.Operation = RowOperation.Modify; table.Rows.Add(modifiedRow); } } } else // added column { table.Definition.Columns[columnName].Added = true; } } } return transform; }
/// <summary> /// Updates the summary information of a database with the current time. /// </summary> /// <param name="db">Database to update.</param> private void UpdateSummaryInfo(Database db) { string now = DateTime.Now.ToString(CultureInfo.InvariantCulture); using (SummaryInformation summary = new SummaryInformation(db)) { if ("{????????-????-????-????-????????????}" == summary.GetProperty(9).ToString()) { summary.SetProperty(9, GenerateGuid()); } summary.SetProperty(12, now); summary.SetProperty(13, now); Version currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; summary.SetProperty(18, String.Format("Windows Installer XML v{0} (candle/light)", currentVersion.ToString())); summary.Close(true); } }