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))); }
internal static MsiPropertyInfo[] GetPropertiesFromDatabase(Database msidb) { List <MsiPropertyInfo> properties = new List <MsiPropertyInfo>(); using (SummaryInformation summaryInfo = new SummaryInformation(msidb)) { foreach (MsiPropertyInfo prototype in DefaultMsiPropertySet) { bool failed = false; object propValue = null; try { // Wix has a bug when translating a FILETIME, so we do it manually if (prototype.PropertyType == VT.FILETIME) { propValue = summaryInfo.GetPropertyFileTime(prototype.ID); } else { propValue = summaryInfo.GetProperty(prototype.ID); } } catch { failed = true; } if (!failed) { properties.Add(new MsiPropertyInfo(prototype, propValue)); } } } return(properties.ToArray()); }
/// <summary> /// Processes the Msp packages to add properties and payloads from the Msp packages. /// </summary> public void Execute() { var packagePayload = this.AuthoredPayloads[this.Facade.PackageSymbol.PayloadRef]; var mspPackage = (WixBundleMspPackageSymbol)this.Facade.SpecificPackageSymbol; var sourcePath = packagePayload.SourceFile.Path; try { using (var db = new Database(sourcePath, OpenDatabase.ReadOnly | OpenDatabase.OpenPatchFile)) { // Read data out of the msp database... using (var sumInfo = new SummaryInformation(db)) { var patchCode = sumInfo.GetProperty(SummaryInformation.Patch.PatchCode); mspPackage.PatchCode = patchCode.Substring(0, 38); } using (var view = db.OpenView(PatchMetadataQuery)) { if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName)) { this.Facade.PackageSymbol.DisplayName = ProcessMspPackageCommand.GetPatchMetadataProperty(view, "DisplayName"); } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Description)) { this.Facade.PackageSymbol.Description = ProcessMspPackageCommand.GetPatchMetadataProperty(view, "Description"); } mspPackage.Manufacturer = ProcessMspPackageCommand.GetPatchMetadataProperty(view, "ManufacturerName"); } } this.ProcessPatchXml(packagePayload, mspPackage, sourcePath); } catch (MsiException e) { this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(packagePayload.SourceLineNumbers, sourcePath, e.Message)); return; } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) { this.Facade.PackageSymbol.CacheId = mspPackage.PatchCode; } }
public WindowsInstallerData Execute() { this.exportedFiles = new List <string>(); string modularizationGuid = null; var output = new WindowsInstallerData(new SourceLineNumber(this.DatabasePath)); View validationView = null; // set the output type output.Type = this.OutputType; Directory.CreateDirectory(this.IntermediateFolder); // get the codepage this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt"); using (var sr = File.OpenText(Path.Combine(this.IntermediateFolder, "_ForceCodepage.idt"))) { string line; while (null != (line = sr.ReadLine())) { var 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 (!this.SkipSummaryInfo) { using (var summaryInformation = new SummaryInformation(this.Database)) { var table = new Table(this.TableDefinitions["_SummaryInformation"]); for (var i = 1; 19 >= i; i++) { var value = summaryInformation.GetProperty(i); if (0 < value.Length) { var 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 (this.Database.TableExists("_Validation")) { validationView = this.Database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?"); } // get the normal tables using (var tablesView = this.Database.OpenExecuteView("SELECT * FROM _Tables")) { foreach (var tableRecord in tablesView.Records) { var tableName = tableRecord.GetString(1); using (var tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) { var tableDefinition = this.GetTableDefinition(tableName, tableView, validationView); var table = new Table(tableDefinition); foreach (var rowRecord in tableView.Records) { var recordCount = rowRecord.GetFieldCount(); var row = table.CreateRow(output.SourceLineNumbers); for (var i = 0; recordCount > i && row.Fields.Length > i; i++) { if (rowRecord.IsNull(i + 1)) { if (!row.Fields[i].Column.Nullable) { // 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: var success = false; var 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.Messaging.Write(WarningMessages.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name)); } break; case ColumnType.Object: var sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES"; if (null != this.ExportBasePath) { var relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.')); sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile); // ensure the parent directory exists System.IO.Directory.CreateDirectory(Path.Combine(this.ExportBasePath, tableName)); using (var fs = System.IO.File.Create(sourceFile)) { int bytesRead; var buffer = new byte[512]; while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length))) { fs.Write(buffer, 0, bytesRead); } } this.exportedFiles.Add(sourceFile); } row[i] = sourceFile; break; default: var 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) { var 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) { var 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 // TODO: Evaluate this requirement. //var matches = Common.WixVariableRegex.Matches(value); //for (var 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) { var table = output.Tables["_SummaryInformation"]; foreach (var row in table.Rows) { if (9 == (int)row[0]) // PID_REVNUMBER { row[1] = modularizationGuid; } } } if (this.IsAdminImage) { this.GenerateWixFileTable(this.DatabasePath, output); this.GenerateSectionIds(output); } return(output); }
public WindowsInstallerData Execute() { var transform = new WindowsInstallerData(new SourceLineNumber(this.TransformFile)); transform.Type = OutputType.Transform; // get the summary information table using (var summaryInformation = new SummaryInformation(this.TransformFile)) { var table = transform.EnsureTable(this.TableDefinitions["_SummaryInformation"]); for (var i = 1; 19 >= i; i++) { var value = summaryInformation.GetProperty(i); if (0 < value.Length) { var row = table.CreateRow(transform.SourceLineNumbers); row[0] = i; row[1] = value; } } } // create a schema msi which hopefully matches the table schemas in the transform var schemaOutput = new WindowsInstallerData(null); var msiDatabaseFile = Path.Combine(this.IntermediateFolder, "schema.msi"); foreach (var tableDefinition in this.TableDefinitions) { // skip unreal tables and the Patch table if (!tableDefinition.Unreal && "Patch" != tableDefinition.Name) { schemaOutput.EnsureTable(tableDefinition); } } var addedRows = new Dictionary <string, Row>(); Table transformViewTable; // Bind the schema msi. this.GenerateDatabase(schemaOutput, msiDatabaseFile); // apply the transform to the database and retrieve the modifications using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { // apply the transform with the ViewTransform option to collect all the modifications msiDatabase.ApplyTransform(this.TransformFile, TransformErrorConditions.All | TransformErrorConditions.ViewTransform); // unbind the database var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); var transformViewOutput = unbindCommand.Execute(); // index the added and possibly modified rows (added rows may also appears as modified rows) transformViewTable = transformViewOutput.Tables["_TransformView"]; var modifiedRows = new Hashtable(); foreach (var row in transformViewTable.Rows) { var tableName = (string)row[0]; var columnName = (string)row[1]; var primaryKeys = (string)row[2]; if ("INSERT" == columnName) { var index = String.Concat(tableName, ':', primaryKeys); addedRows.Add(index, null); } else if ("CREATE" != columnName && "DELETE" != columnName && "DROP" != columnName && null != primaryKeys) // modified row { var 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) { var tableName = (string)row[0]; var columnName = (string)row[1]; var primaryKeys = (string)row[2]; var index = String.Concat(tableName, ':', primaryKeys); // ignore information for added rows if (!addedRows.ContainsKey(index)) { var table = schemaOutput.Tables[tableName]; this.CreateRow(table, primaryKeys, true); } } } // Re-bind the schema output with the placeholder rows. this.GenerateDatabase(schemaOutput, msiDatabaseFile); // apply the transform to the database and retrieve the modifications using (var msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { try { // apply the transform msiDatabase.ApplyTransform(this.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(ErrorMessages.TransformSchemaMismatch()); } } // unbind the database var unbindCommand = new UnbindDatabaseCommand(this.Messaging, msiDatabase, msiDatabaseFile, OutputType.Product, this.ExportBasePath, this.IntermediateFolder, false, false, skipSummaryInfo: true); var output = unbindCommand.Execute(); // index all the rows to easily find modified rows var rows = new Dictionary <string, Row>(); foreach (var table in output.Tables) { foreach (var row in table.Rows) { rows.Add(String.Concat(table.Name, ':', row.GetPrimaryKey('\t', " ")), row); } } // process the _TransformView rows into transform rows foreach (var row in transformViewTable.Rows) { var tableName = (string)row[0]; var columnName = (string)row[1]; var primaryKeys = (string)row[2]; var table = transform.EnsureTable(this.TableDefinitions[tableName]); if ("CREATE" == columnName) // added table { table.Operation = TableOperation.Add; } else if ("DELETE" == columnName) // deleted row { var 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 { var index = String.Concat(tableName, ':', primaryKeys); var addedRow = rows[index]; addedRow.Operation = RowOperation.Add; table.Rows.Add(addedRow); } else if (null != primaryKeys) // modified row { var 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.ContainsKey(index)) { var modifiedRow = rows[index]; // mark the field as modified var indexOfModifiedValue = -1; for (var i = 0; i < modifiedRow.TableDefinition.Columns.Length; ++i) { if (columnName.Equals(modifiedRow.TableDefinition.Columns[i].Name, StringComparison.Ordinal)) { indexOfModifiedValue = i; break; } } 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 { var column = table.Definition.Columns.Single(c => c.Name.Equals(columnName, StringComparison.Ordinal)); column.Added = true; } } } return(transform); }
public Output Execute() { string modularizationGuid = null; Output output = new Output(new SourceLineNumber(this.DatabasePath)); View validationView = null; // set the output type output.Type = this.OutputType; // get the codepage this.Database.Export("_ForceCodepage", this.IntermediateFolder, "_ForceCodepage.idt"); using (StreamReader sr = File.OpenText(Path.Combine(this.IntermediateFolder, "_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 (!this.SkipSummaryInfo) { using (SummaryInformation summaryInformation = new SummaryInformation(this.Database)) { Table table = new Table(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 (this.Database.TableExists("_Validation")) { validationView = this.Database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?"); } // get the normal tables using (View tablesView = this.Database.OpenExecuteView("SELECT * FROM _Tables")) { while (true) { using (Record tableRecord = tablesView.Fetch()) { if (null == tableRecord) { break; } string tableName = tableRecord.GetString(1); using (View tableView = this.Database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) { ColumnDefinition[] columns; using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES), columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES)) { // index the primary keys HashSet <string> tablePrimaryKeys = new HashSet <string>(); using (Record primaryKeysRecord = this.Database.PrimaryKeys(tableName)) { int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount(); for (int i = 1; i <= primaryKeysFieldCount; i++) { tablePrimaryKeys.Add(primaryKeysRecord.GetString(i)); } } int columnCount = columnNameRecord.GetFieldCount(); columns = new ColumnDefinition[columnCount]; 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); int? minValue = null; int? maxValue = null; string keyTable = null; int? keyColumn = null; 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); minValue = validationRecord.IsNull(4) ? null : (int?)validationRecord.GetInteger(4); maxValue = validationRecord.IsNull(5) ? null : (int?)validationRecord.GetInteger(5); keyTable = validationRecord.IsNull(6) ? null : validationRecord.GetString(6); keyColumn = validationRecord.IsNull(7) ? null : (int?)validationRecord.GetInteger(7); category = validationRecord.IsNull(8) ? null : validationRecord.GetString(8); set = validationRecord.IsNull(9) ? null : validationRecord.GetString(9); description = validationRecord.IsNull(10) ? null : validationRecord.GetString(10); // 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; } columns[i - 1] = new ColumnDefinition(columnName, columnType, length, primary, nullable, columnCategory, minValue, maxValue, keyTable, keyColumn, set, description, columnModularizeType, (ColumnType.Localized == columnType), true); } } TableDefinition tableDefinition = new TableDefinition(tableName, columns, false, false); // 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(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.Nullable) { // 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.Messaging.Write(WarningMessages.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 != this.ExportBasePath) { string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.')); sourceFile = Path.Combine(this.ExportBasePath, relativeSourceFile); // ensure the parent directory exists System.IO.Directory.CreateDirectory(Path.Combine(this.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(this.DatabasePath, output); GenerateSectionIds(output); } return(output); }
private bool CreateFacadesForMergeModuleFiles(WixMergeRow wixMergeRow, List <FileFacade> mergeModulesFileFacades, Dictionary <string, FileFacade> indexedFileFacades) { bool containsFiles = false; 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")) { Dictionary <string, FileFacade> uniqueModuleFileIdentifiers = new Dictionary <string, FileFacade>(StringComparer.OrdinalIgnoreCase); 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 = (FileRow)this.FileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); fileRow.File = record[1]; fileRow.Compressed = wixMergeRow.FileCompression; WixFileRow wixFileRow = (WixFileRow)this.WixFileTable.CreateRow(wixMergeRow.SourceLineNumbers, false); wixFileRow.Directory = record[2]; wixFileRow.DiskId = wixMergeRow.DiskId; wixFileRow.PatchGroup = -1; wixFileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture), Path.DirectorySeparatorChar, record[1]); FileFacade mergeModuleFileFacade = new FileFacade(true, fileRow, wixFileRow); FileFacade collidingFacade; // If case-sensitive collision with another merge module or a user-authored file identifier. if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) { Messaging.Instance.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFacade.File.File)); } else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.File.File, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module { Messaging.Instance.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, mergeModuleFileFacade.File.File, collidingFacade.File.File)); } else // no collision { mergeModulesFileFacades.Add(mergeModuleFileFacade); // Keep updating the indexes as new rows are added. indexedFileFacades.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade); uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.File.File, mergeModuleFileFacade); } 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 > this.OutputInstallerVersion) { Messaging.Instance.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, this.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)); } return(containsFiles); }
/// <summary> /// Processes the MSI packages to add properties and payloads from the MSI packages. /// </summary> public void Execute() { var packagePayload = this.PackagePayloads[this.Facade.PackageSymbol.PayloadRef]; var msiPackage = (WixBundleMsiPackageSymbol)this.Facade.SpecificPackageSymbol; var sourcePath = packagePayload.SourceFile.Path; var longNamesInImage = false; var compressed = false; try { using (var db = new Database(sourcePath, OpenDatabase.ReadOnly)) { // Read data out of the msi database... using (var sumInfo = new SummaryInformation(db)) { var fileAndElevateFlags = sumInfo.GetNumericProperty(SummaryInformation.Package.FileAndElevatedFlags); var platformsAndLanguages = sumInfo.GetProperty(SummaryInformation.Package.PlatformsAndLanguages); // 1 is the Word Count summary information stream bit that means // the MSI uses short file names when set. We care about long file // names so check when the bit is not set. longNamesInImage = 0 == (fileAndElevateFlags & 1); // 2 is the Word Count summary information stream bit that means // files are compressed in the MSI by default when the bit is set. compressed = 2 == (fileAndElevateFlags & 2); // 8 is the Word Count summary information stream bit that means // "Elevated privileges are not required to install this package." // in MSI 4.5 and below, if this bit is 0, elevation is required. var perMachine = (0 == (fileAndElevateFlags & 8)); var x64 = platformsAndLanguages.Contains("x64"); this.Facade.PackageSymbol.PerMachine = perMachine ? YesNoDefaultType.Yes : YesNoDefaultType.No; this.Facade.PackageSymbol.Win64 = x64; } string packageName = null; string packageDescription = null; string allusers = null; string fastInstall = null; string systemComponent = null; using (var view = db.OpenView(PropertySqlQuery)) { packageName = ProcessMsiPackageCommand.GetProperty(view, "ProductName"); packageDescription = ProcessMsiPackageCommand.GetProperty(view, "ARPCOMMENTS"); allusers = ProcessMsiPackageCommand.GetProperty(view, "ALLUSERS"); fastInstall = ProcessMsiPackageCommand.GetProperty(view, "MSIFASTINSTALL"); systemComponent = ProcessMsiPackageCommand.GetProperty(view, "ARPSYSTEMCOMPONENT"); msiPackage.ProductCode = ProcessMsiPackageCommand.GetProperty(view, "ProductCode"); msiPackage.UpgradeCode = ProcessMsiPackageCommand.GetProperty(view, "UpgradeCode"); msiPackage.Manufacturer = ProcessMsiPackageCommand.GetProperty(view, "Manufacturer"); msiPackage.ProductLanguage = Convert.ToInt32(ProcessMsiPackageCommand.GetProperty(view, "ProductLanguage"), CultureInfo.InvariantCulture); msiPackage.ProductVersion = ProcessMsiPackageCommand.GetProperty(view, "ProductVersion"); } if (!this.BackendHelper.IsValidFourPartVersion(msiPackage.ProductVersion)) { // not a proper .NET version (e.g., five fields); can we get a valid four-part version number? string version = null; var versionParts = msiPackage.ProductVersion.Split('.'); var count = versionParts.Length; if (0 < count) { version = versionParts[0]; for (var i = 1; i < 4 && i < count; ++i) { version = String.Concat(version, ".", versionParts[i]); } } if (!String.IsNullOrEmpty(version) && this.BackendHelper.IsValidFourPartVersion(version)) { this.Messaging.Write(WarningMessages.VersionTruncated(this.Facade.PackageSymbol.SourceLineNumbers, msiPackage.ProductVersion, sourcePath, version)); msiPackage.ProductVersion = version; } else { this.Messaging.Write(ErrorMessages.InvalidProductVersion(this.Facade.PackageSymbol.SourceLineNumbers, msiPackage.ProductVersion, sourcePath)); } } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.CacheId)) { this.Facade.PackageSymbol.CacheId = String.Format("{0}v{1}", msiPackage.ProductCode, msiPackage.ProductVersion); } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.DisplayName)) { this.Facade.PackageSymbol.DisplayName = packageName; } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Description)) { this.Facade.PackageSymbol.Description = packageDescription; } if (String.IsNullOrEmpty(this.Facade.PackageSymbol.Version)) { this.Facade.PackageSymbol.Version = msiPackage.ProductVersion; } var payloadNames = this.GetPayloadTargetNames(); var msiPropertyNames = this.GetMsiPropertyNames(packagePayload.Id.Id); this.SetPerMachineAppropriately(allusers, msiPackage, sourcePath); // Ensure the MSI package is appropriately marked visible or not. this.SetPackageVisibility(systemComponent, msiPackage, msiPropertyNames); // Unless the MSI or setup code overrides the default, set MSIFASTINSTALL for best performance. if (!String.IsNullOrEmpty(fastInstall)) { this.AddMsiProperty(msiPackage, "MSIFASTINSTALL", "7"); } this.CreateRelatedPackages(db); // If feature selection is enabled, represent the Feature table in the manifest. if ((msiPackage.Attributes & WixBundleMsiPackageAttributes.EnableFeatureSelection) == WixBundleMsiPackageAttributes.EnableFeatureSelection) { this.CreateMsiFeatures(db); } // Add all external cabinets as package payloads. this.ImportExternalCabinetAsPayloads(db, packagePayload, payloadNames); // Add all external files as package payloads and calculate the total install size as the rollup of // File table's sizes. this.Facade.PackageSymbol.InstallSize = this.ImportExternalFileAsPayloadsAndReturnInstallSize(db, packagePayload, longNamesInImage, compressed, payloadNames); // Add all dependency providers from the MSI. this.ImportDependencyProviders(db, msiPackage); } } catch (MsiException e) { this.Messaging.Write(ErrorMessages.UnableToReadPackageInformation(this.Facade.PackageSymbol.SourceLineNumbers, sourcePath, e.Message)); } }
private bool CreateFacadesForMergeModuleFiles(WixMergeSymbol wixMergeRow, List <FileFacade> mergeModulesFileFacades, Dictionary <string, FileFacade> indexedFileFacades) { var containsFiles = false; try { // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module. using (var db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly)) { if (db.TableExists("File") && db.TableExists("Component")) { var uniqueModuleFileIdentifiers = new Dictionary <string, FileFacade>(StringComparer.OrdinalIgnoreCase); using (var 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) foreach (var record in view.Records) { // 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. var fileSymbol = new FileSymbol(wixMergeRow.SourceLineNumbers, new Identifier(AccessModifier.Private, record[1])); fileSymbol.Attributes = wixMergeRow.FileAttributes; fileSymbol.DirectoryRef = record[2]; fileSymbol.DiskId = wixMergeRow.DiskId; fileSymbol.Source = new IntermediateFieldPathValue { Path = Path.Combine(this.IntermediateFolder, wixMergeRow.Id.Id, record[1]) }; var mergeModuleFileFacade = new FileFacade(true, fileSymbol); // If case-sensitive collision with another merge module or a user-authored file identifier. if (indexedFileFacades.TryGetValue(mergeModuleFileFacade.Id, out var collidingFacade)) { this.Messaging.Write(ErrorMessages.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, collidingFacade.Id)); } else if (uniqueModuleFileIdentifiers.TryGetValue(mergeModuleFileFacade.Id, out collidingFacade)) // case-insensitive collision with another file identifier in the same merge module { this.Messaging.Write(ErrorMessages.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, mergeModuleFileFacade.Id, collidingFacade.Id)); } else // no collision { mergeModulesFileFacades.Add(mergeModuleFileFacade); // Keep updating the indexes as new rows are added. indexedFileFacades.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); uniqueModuleFileIdentifiers.Add(mergeModuleFileFacade.Id, mergeModuleFileFacade); } containsFiles = true; } } } // Get the summary information to detect the Schema using (var summaryInformation = new SummaryInformation(db)) { var moduleInstallerVersionString = summaryInformation.GetProperty(14); try { var moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture); if (moduleInstallerVersion > this.OutputInstallerVersion) { this.Messaging.Write(WarningMessages.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, moduleInstallerVersion, this.OutputInstallerVersion)); } } catch (FormatException) { throw new WixException(ErrorMessages.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile, moduleInstallerVersionString)); } } } } catch (FileNotFoundException) { throw new WixException(ErrorMessages.FileNotFound(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile)); } catch (Win32Exception) { throw new WixException(ErrorMessages.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id.Id, wixMergeRow.SourceFile)); } return(containsFiles); }
/// <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); } } // bind the schema msi Binder binder = new Binder(); binder.SuppressAddingValidationRows = true; binder.WixVariableResolver = new WixVariableResolver(); binder.GenerateDatabase(schemaOutput, msiDatabaseFile); // apply the transform to the database and retrieve the modifications Hashtable addedRows = new Hashtable(); Table transformViewTable; 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); // 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); // apply the transform to the database and retrieve the modifications using (Database msiDatabase = new Database(msiDatabaseFile, OpenDatabase.Transact)) { // apply the transform msiDatabase.ApplyTransform(transformFile, TransformErrorConditions.All); // commit the database to guard against weird errors with streams msiDatabase.Commit(); // unbind the database Output output = this.UnbindDatabase(msiDatabaseFile, msiDatabase, OutputType.Product, exportBasePath); // 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> /// 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> /// <returns>The output representing the database.</returns> private Output UnbindDatabase(string databaseFile, Database database, OutputType outputType, string exportBasePath) { 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 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")) { Record tableRecord; while (null != (tableRecord = tablesView.Fetch())) { string tableName = tableRecord.GetString(1); using (View tableView = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName))) { Record rowRecord; Table table; TableDefinition tableDefinition; if (this.tableDefinitions.Contains(tableName)) { tableDefinition = this.tableDefinitions[tableName]; // TODO: verify table definitions // - error for different column name or data type // - warn for additional columns // - need extra info for new columns in standard tables (MSI 4.0 changes) } else // custom table { TableDefinition customTableDefinition = new TableDefinition(tableName, false, false); Hashtable customTablePrimaryKeys = new Hashtable(); Record customColumnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES); Record customColumnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES); int customColumnCount = customColumnNameRecord.GetFieldCount(); // index the primary keys using (Record primaryKeysRecord = database.PrimaryKeys(tableName)) { int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount(); for (int i = 1; i <= primaryKeysFieldCount; i++) { customTablePrimaryKeys[primaryKeysRecord.GetString(i)] = null; } } for (int i = 1; i <= customColumnCount; i++) { string columnName = customColumnNameRecord.GetString(i); string idtType = customColumnTypeRecord.GetString(i); ColumnType columnType; int length; bool nullable; ColumnCategory columnCategory = ColumnCategory.Unknown; ColumnModularizeType columnModularizeType = ColumnModularizeType.None; bool primary = customTablePrimaryKeys.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) { Record validationRecord = new Record(2); validationRecord.SetString(1, tableName); validationRecord.SetString(2, columnName); validationView.Execute(validationRecord); validationRecord.Close(); if (null != (validationRecord = validationView.Fetch())) { 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; } } validationRecord.Close(); } 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) { columnModularizeType = ColumnModularizeType.Property; } else if (ColumnCategory.Identifier == columnCategory) { columnModularizeType = ColumnModularizeType.Column; } customTableDefinition.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)); } tableDefinition = customTableDefinition; customColumnNameRecord.Close(); customColumnTypeRecord.Close(); } table = new Table(null, tableDefinition); while (null != (rowRecord = tableView.Fetch())) { 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: if (row.Fields[i].Column.IsLocalizable) { row[i] = Convert.ToString(rowRecord.GetInteger(i + 1), CultureInfo.InvariantCulture); } else { row[i] = rowRecord.GetInteger(i + 1); } 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; } } } rowRecord.Close(); } output.Tables.Add(table); } tableRecord.Close(); } } } 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; } } } return(output); }