Пример #1
0
        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());
        }
Пример #3
0
        /// <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);
        }
Пример #5
0
        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);
        }
Пример #6
0
        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);
        }
Пример #7
0
        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);
        }
Пример #8
0
        /// <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);
        }
Пример #10
0
        /// <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);
        }
Пример #11
0
        /// <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);
        }