Пример #1
0
        public Row CreateRow(IntermediateSection section, IntermediateSymbol symbol, WindowsInstallerData output, TableDefinition tableDefinition)
        {
            var table = output.EnsureTable(tableDefinition);

            var row = table.CreateRow(symbol.SourceLineNumbers);
            row.SectionId = section.Id;

            return row;
        }
        /// <summary>
        /// Generates the WixFile table based on a path to an admin image msi and an Output.
        /// </summary>
        /// <param name="databaseFile">The path to the msi database file in an admin image.</param>
        /// <param name="output">The Output that represents the msi database.</param>
        private void GenerateWixFileTable(string databaseFile, WindowsInstallerData output)
        {
            throw new NotImplementedException();
#if TODO_FIX_UNBINDING_FILES
            var adminRootPath = Path.GetDirectoryName(databaseFile);

            var componentDirectoryIndex = new Hashtable();
            var componentTable          = output.Tables["Component"];
            foreach (var row in componentTable.Rows)
            {
                componentDirectoryIndex.Add(row[0], row[2]);
            }

            // Index full source paths for all directories
            var directoryDirectoryParentIndex = new Hashtable();
            var directoryFullPathIndex        = new Hashtable();
            var directorySourceNameIndex      = new Hashtable();
            var directoryTable = output.Tables["Directory"];
            foreach (var row in directoryTable.Rows)
            {
                directoryDirectoryParentIndex.Add(row[0], row[1]);
                if (null == row[1])
                {
                    directoryFullPathIndex.Add(row[0], adminRootPath);
                }
                else
                {
                    directorySourceNameIndex.Add(row[0], GetAdminSourceName((string)row[2]));
                }
            }

            foreach (DictionaryEntry directoryEntry in directoryDirectoryParentIndex)
            {
                if (!directoryFullPathIndex.ContainsKey(directoryEntry.Key))
                {
                    this.GetAdminFullPath((string)directoryEntry.Key, directoryDirectoryParentIndex, directorySourceNameIndex, directoryFullPathIndex);
                }
            }

            var fileTable    = output.Tables["File"];
            var wixFileTable = output.EnsureTable(this.TableDefinitions["WixFile"]);
            foreach (var row in fileTable.Rows)
            {
                var wixFileRow = new WixFileRow(null, this.TableDefinitions["WixFile"]);
                wixFileRow.File      = (string)row[0];
                wixFileRow.Directory = (string)componentDirectoryIndex[(string)row[1]];
                wixFileRow.Source    = Path.Combine((string)directoryFullPathIndex[wixFileRow.Directory], GetAdminSourceName((string)row[2]));

                if (!File.Exists(wixFileRow.Source))
                {
                    throw new WixException(ErrorMessages.WixFileNotFound(wixFileRow.Source));
                }

                wixFileTable.Rows.Add(wixFileRow);
            }
#endif
        }
        public void Execute()
        {
            var transformFlags = 0;

            var targetOutput  = new WindowsInstallerData(null);
            var updatedOutput = new WindowsInstallerData(null);

            // TODO: handle added columns

            // to generate a localized transform, both the target and updated
            // databases need to have the same code page. the only reason to
            // set different code pages is to support localized primary key
            // columns, but that would only support deleting rows. if this
            // becomes necessary, define a PreviousCodepage property on the
            // Output class and persist this throughout transform generation.
            targetOutput.Codepage  = this.Transform.Codepage;
            updatedOutput.Codepage = this.Transform.Codepage;

            // remove certain Property rows which will be populated from summary information values
            string targetUpgradeCode  = null;
            string updatedUpgradeCode = null;

            if (this.Transform.TryGetTable("Property", out var propertyTable))
            {
                for (int i = propertyTable.Rows.Count - 1; i >= 0; i--)
                {
                    Row row = propertyTable.Rows[i];

                    if ("ProductCode" == (string)row[0] || "ProductLanguage" == (string)row[0] || "ProductVersion" == (string)row[0] || "UpgradeCode" == (string)row[0])
                    {
                        propertyTable.Rows.RemoveAt(i);

                        if ("UpgradeCode" == (string)row[0])
                        {
                            updatedUpgradeCode = (string)row[1];
                        }
                    }
                }
            }

            var targetSummaryInfo    = targetOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
            var updatedSummaryInfo   = updatedOutput.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
            var targetPropertyTable  = targetOutput.EnsureTable(this.TableDefinitions["Property"]);
            var updatedPropertyTable = updatedOutput.EnsureTable(this.TableDefinitions["Property"]);

            // process special summary information values
            foreach (var row in this.Transform.Tables["_SummaryInformation"].Rows)
            {
                var summaryId   = row.FieldAsInteger(0);
                var summaryData = row.FieldAsString(1);

                if ((int)SummaryInformation.Transform.CodePage == summaryId)
                {
                    // convert from a web name if provided
                    var codePage = summaryData;
                    if (null == codePage)
                    {
                        codePage = "0";
                    }
                    else
                    {
                        codePage = Common.GetValidCodePage(codePage).ToString(CultureInfo.InvariantCulture);
                    }

                    var previousCodePage = row.Fields[1].PreviousData;
                    if (null == previousCodePage)
                    {
                        previousCodePage = "0";
                    }
                    else
                    {
                        previousCodePage = Common.GetValidCodePage(previousCodePage).ToString(CultureInfo.InvariantCulture);
                    }

                    var targetCodePageRow = targetSummaryInfo.CreateRow(null);
                    targetCodePageRow[0] = 1; // PID_CODEPAGE
                    targetCodePageRow[1] = previousCodePage;

                    var updatedCodePageRow = updatedSummaryInfo.CreateRow(null);
                    updatedCodePageRow[0] = 1; // PID_CODEPAGE
                    updatedCodePageRow[1] = codePage;
                }
                else if ((int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ||
                         (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == summaryId)
                {
                    // the target language
                    var propertyData = summaryData.Split(';');
                    var lang         = 2 == propertyData.Length ? propertyData[1] : "0";

                    var tempSummaryInfo   = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetSummaryInfo : updatedSummaryInfo;
                    var tempPropertyTable = (int)SummaryInformation.Transform.TargetPlatformAndLanguage == summaryId ? targetPropertyTable : updatedPropertyTable;

                    var productLanguageRow = tempPropertyTable.CreateRow(null);
                    productLanguageRow[0] = "ProductLanguage";
                    productLanguageRow[1] = lang;

                    // set the platform;language on the MSI to be generated
                    var templateRow = tempSummaryInfo.CreateRow(null);
                    templateRow[0] = 7; // PID_TEMPLATE
                    templateRow[1] = summaryData;
                }
                else if ((int)SummaryInformation.Transform.ProductCodes == summaryId)
                {
                    var propertyData = summaryData.Split(';');

                    var targetProductCodeRow = targetPropertyTable.CreateRow(null);
                    targetProductCodeRow[0] = "ProductCode";
                    targetProductCodeRow[1] = propertyData[0].Substring(0, 38);

                    var targetProductVersionRow = targetPropertyTable.CreateRow(null);
                    targetProductVersionRow[0] = "ProductVersion";
                    targetProductVersionRow[1] = propertyData[0].Substring(38);

                    var updatedProductCodeRow = updatedPropertyTable.CreateRow(null);
                    updatedProductCodeRow[0] = "ProductCode";
                    updatedProductCodeRow[1] = propertyData[1].Substring(0, 38);

                    var updatedProductVersionRow = updatedPropertyTable.CreateRow(null);
                    updatedProductVersionRow[0] = "ProductVersion";
                    updatedProductVersionRow[1] = propertyData[1].Substring(38);

                    // UpgradeCode is optional and may not exists in the target
                    // or upgraded databases, so do not include a null-valued
                    // UpgradeCode property.

                    targetUpgradeCode = propertyData[2];
                    if (!String.IsNullOrEmpty(targetUpgradeCode))
                    {
                        var targetUpgradeCodeRow = targetPropertyTable.CreateRow(null);
                        targetUpgradeCodeRow[0] = "UpgradeCode";
                        targetUpgradeCodeRow[1] = targetUpgradeCode;

                        // If the target UpgradeCode is specified, an updated
                        // UpgradeCode is required.
                        if (String.IsNullOrEmpty(updatedUpgradeCode))
                        {
                            updatedUpgradeCode = targetUpgradeCode;
                        }
                    }

                    if (!String.IsNullOrEmpty(updatedUpgradeCode))
                    {
                        var updatedUpgradeCodeRow = updatedPropertyTable.CreateRow(null);
                        updatedUpgradeCodeRow[0] = "UpgradeCode";
                        updatedUpgradeCodeRow[1] = updatedUpgradeCode;
                    }
                }
                else if ((int)SummaryInformation.Transform.ValidationFlags == summaryId)
                {
                    transformFlags = Convert.ToInt32(summaryData, CultureInfo.InvariantCulture);
                }
                else if ((int)SummaryInformation.Transform.Reserved11 == summaryId)
                {
                    // PID_LASTPRINTED should be null for transforms
                    row.Operation = RowOperation.None;
                }
                else
                {
                    // add everything else as is
                    var targetRow = targetSummaryInfo.CreateRow(null);
                    targetRow[0] = row[0];
                    targetRow[1] = row[1];

                    var updatedRow = updatedSummaryInfo.CreateRow(null);
                    updatedRow[0] = row[0];
                    updatedRow[1] = row[1];
                }
            }

            // Validate that both databases have an UpgradeCode if the
            // authoring transform will validate the UpgradeCode; otherwise,
            // MsiCreateTransformSummaryinfo() will fail with 1620.
            if (((int)TransformFlags.ValidateUpgradeCode & transformFlags) != 0 &&
                (String.IsNullOrEmpty(targetUpgradeCode) || String.IsNullOrEmpty(updatedUpgradeCode)))
            {
                this.Messaging.Write(ErrorMessages.BothUpgradeCodesRequired());
            }

            string emptyFile = null;

            foreach (var table in this.Transform.Tables)
            {
                // Ignore unreal tables when building transforms except the _Stream table.
                // These tables are ignored when generating the database so there is no reason
                // to process them here.
                if (table.Definition.Unreal && "_Streams" != table.Name)
                {
                    continue;
                }

                // process table operations
                switch (table.Operation)
                {
                case TableOperation.Add:
                    updatedOutput.EnsureTable(table.Definition);
                    break;

                case TableOperation.Drop:
                    targetOutput.EnsureTable(table.Definition);
                    continue;

                default:
                    targetOutput.EnsureTable(table.Definition);
                    updatedOutput.EnsureTable(table.Definition);
                    break;
                }

                // process row operations
                foreach (var row in table.Rows)
                {
                    switch (row.Operation)
                    {
                    case RowOperation.Add:
                        var updatedTable = updatedOutput.EnsureTable(table.Definition);
                        updatedTable.Rows.Add(row);
                        continue;

                    case RowOperation.Delete:
                        var targetTable = targetOutput.EnsureTable(table.Definition);
                        targetTable.Rows.Add(row);

                        // fill-in non-primary key values
                        foreach (var field in row.Fields)
                        {
                            if (!field.Column.PrimaryKey)
                            {
                                if (ColumnType.Number == field.Column.Type && !field.Column.IsLocalizable)
                                {
                                    field.Data = field.Column.MinValue;
                                }
                                else if (ColumnType.Object == field.Column.Type)
                                {
                                    if (null == emptyFile)
                                    {
                                        emptyFile = Path.Combine(this.IntermediateFolder, "empty");
                                    }

                                    field.Data = emptyFile;
                                }
                                else
                                {
                                    field.Data = "0";
                                }
                            }
                        }
                        continue;
                    }

                    // Assure that the file table's sequence is populated
                    if ("File" == table.Name)
                    {
                        foreach (var fileRow in table.Rows)
                        {
                            if (null == fileRow[7])
                            {
                                if (RowOperation.Add == fileRow.Operation)
                                {
                                    this.Messaging.Write(ErrorMessages.InvalidAddedFileRowWithoutSequence(fileRow.SourceLineNumbers, (string)fileRow[0]));
                                    break;
                                }

                                // Set to 1 to prevent invalid IDT file from being generated
                                fileRow[7] = 1;
                            }
                        }
                    }

                    // process modified and unmodified rows
                    var modifiedRow = false;
                    var targetRow   = table.Definition.CreateRow(null);
                    var updatedRow  = row;
                    for (var i = 0; i < row.Fields.Length; i++)
                    {
                        var updatedField = row.Fields[i];

                        if (updatedField.Modified)
                        {
                            // set a different value in the target row to ensure this value will be modified during transform generation
                            if (ColumnType.Number == updatedField.Column.Type && !updatedField.Column.IsLocalizable)
                            {
                                var data = updatedField.AsNullableInteger();
                                targetRow[i] = (data == 1) ? 2 : 1;
                            }
                            else if (ColumnType.Object == updatedField.Column.Type)
                            {
                                if (null == emptyFile)
                                {
                                    emptyFile = Path.Combine(this.IntermediateFolder, "empty");
                                }

                                targetRow[i] = emptyFile;
                            }
                            else
                            {
                                var data = updatedField.AsString();
                                targetRow[i] = (data == "0") ? "1" : "0";
                            }

                            modifiedRow = true;
                        }
                        else if (ColumnType.Object == updatedField.Column.Type)
                        {
                            var objectField = (ObjectField)updatedField;

                            // create an empty file for comparing against
                            if (null == objectField.PreviousData)
                            {
                                if (null == emptyFile)
                                {
                                    emptyFile = Path.Combine(this.IntermediateFolder, "empty");
                                }

                                targetRow[i] = emptyFile;
                                modifiedRow  = true;
                            }
                            else if (!this.FileSystemManager.CompareFiles(objectField.PreviousData, (string)objectField.Data))
                            {
                                targetRow[i] = objectField.PreviousData;
                                modifiedRow  = true;
                            }
                        }
                        else // unmodified
                        {
                            if (null != updatedField.Data)
                            {
                                targetRow[i] = updatedField.Data;
                            }
                        }
                    }

                    // modified rows and certain special rows go in the target and updated msi databases
                    if (modifiedRow ||
                        ("Property" == table.Name &&
                         ("ProductCode" == (string)row[0] ||
                          "ProductLanguage" == (string)row[0] ||
                          "ProductVersion" == (string)row[0] ||
                          "UpgradeCode" == (string)row[0])))
                    {
                        var targetTable = targetOutput.EnsureTable(table.Definition);
                        targetTable.Rows.Add(targetRow);

                        var updatedTable = updatedOutput.EnsureTable(table.Definition);
                        updatedTable.Rows.Add(updatedRow);
                    }
                }
            }

            //foreach (BinderExtension extension in this.Extensions)
            //{
            //    extension.PostBind(this.Context);
            //}

            // Any errors encountered up to this point can cause errors during generation.
            if (this.Messaging.EncounteredError)
            {
                return;
            }

            var transformFileName   = Path.GetFileNameWithoutExtension(this.OutputPath);
            var targetDatabaseFile  = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_target.msi"));
            var updatedDatabaseFile = Path.Combine(this.IntermediateFolder, String.Concat(transformFileName, "_updated.msi"));

            try
            {
                if (!String.IsNullOrEmpty(emptyFile))
                {
                    using (var fileStream = File.Create(emptyFile))
                    {
                    }
                }

                this.GenerateDatabase(targetOutput, targetDatabaseFile, keepAddedColumns: false);
                this.GenerateDatabase(updatedOutput, updatedDatabaseFile, keepAddedColumns: true);

                // make sure the directory exists
                Directory.CreateDirectory(Path.GetDirectoryName(this.OutputPath));

                // create the transform file
                using (var targetDatabase = new Database(targetDatabaseFile, OpenDatabase.ReadOnly))
                    using (var updatedDatabase = new Database(updatedDatabaseFile, OpenDatabase.ReadOnly))
                    {
                        if (updatedDatabase.GenerateTransform(targetDatabase, this.OutputPath))
                        {
                            updatedDatabase.CreateTransformSummaryInfo(targetDatabase, this.OutputPath, (TransformErrorConditions)(transformFlags & 0xFFFF), (TransformValidations)((transformFlags >> 16) & 0xFFFF));
                        }
                        else
                        {
                            this.Messaging.Write(ErrorMessages.NoDifferencesInTransform(this.Transform.SourceLineNumbers));
                        }
                    }
            }
            finally
            {
                if (!String.IsNullOrEmpty(emptyFile))
                {
                    File.Delete(emptyFile);
                }
            }
        }
Пример #4
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);
        }
Пример #5
0
        /// <summary>
        /// Creates a transform by diffing two outputs.
        /// </summary>
        public WindowsInstallerData Execute()
        {
            var targetOutput    = this.TargetOutput;
            var updatedOutput   = this.UpdatedOutput;
            var validationFlags = this.ValidationFlags;

            var transform = new WindowsInstallerData(null)
            {
                Type     = OutputType.Transform,
                Codepage = updatedOutput.Codepage
            };

            this.transformSummaryInfo = new SummaryInformationStreams();

            // compare the codepages
            if (targetOutput.Codepage != updatedOutput.Codepage && 0 == (TransformFlags.ErrorChangeCodePage & validationFlags))
            {
                this.messaging.Write(ErrorMessages.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage));
                if (null != updatedOutput.SourceLineNumbers)
                {
                    this.messaging.Write(ErrorMessages.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers));
                }
            }

            // compare the output types
            if (targetOutput.Type != updatedOutput.Type)
            {
                throw new WixException(ErrorMessages.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString()));
            }

            // compare the contents of the tables
            foreach (var targetTable in targetOutput.Tables)
            {
                var updatedTable = updatedOutput.Tables[targetTable.Name];
                var operation    = TableOperation.None;

                var rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation);

                if (TableOperation.Drop == operation)
                {
                    var droppedTable = transform.EnsureTable(targetTable.Definition);
                    droppedTable.Operation = TableOperation.Drop;
                }
                else if (TableOperation.None == operation)
                {
                    var modifiedTable = transform.EnsureTable(updatedTable.Definition);
                    foreach (var row in rows)
                    {
                        modifiedTable.Rows.Add(row);
                    }
                }
            }

            // added tables
            foreach (var updatedTable in updatedOutput.Tables)
            {
                if (null == targetOutput.Tables[updatedTable.Name])
                {
                    var addedTable = transform.EnsureTable(updatedTable.Definition);
                    addedTable.Operation = TableOperation.Add;

                    foreach (var updatedRow in updatedTable.Rows)
                    {
                        updatedRow.Operation = RowOperation.Add;
                        updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
                        addedTable.Rows.Add(updatedRow);
                    }
                }
            }

            // set summary information properties
            if (!this.SuppressKeepingSpecialRows)
            {
                var summaryInfoTable = transform.Tables["_SummaryInformation"];
                this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags);
            }

            this.Transform = transform;
            return(this.Transform);
        }
        /// <summary>
        /// Adds the PatchFiles action to the sequence table if it does not already exist.
        /// </summary>
        /// <param name="table">The sequence table to check or modify.</param>
        /// <param name="mainTransform">The primary authoring transform.</param>
        /// <param name="pairedTransform">The secondary patch transform.</param>
        /// <param name="mainFileRow">The file row that contains information about the patched file.</param>
        private void AddPatchFilesActionToSequenceTable(SequenceTable table, WindowsInstallerData mainTransform, WindowsInstallerData pairedTransform, Row mainFileRow)
        {
            var tableName = table.ToString();

            // Find/add PatchFiles action (also determine sequence for it).
            // Search mainTransform first, then pairedTransform (pairedTransform overrides).
            var hasPatchFilesAction    = false;
            var installFilesSequence   = 0;
            var duplicateFilesSequence = 0;

            TestSequenceTableForPatchFilesAction(
                mainTransform.Tables[tableName],
                ref hasPatchFilesAction,
                ref installFilesSequence,
                ref duplicateFilesSequence);
            TestSequenceTableForPatchFilesAction(
                pairedTransform.Tables[tableName],
                ref hasPatchFilesAction,
                ref installFilesSequence,
                ref duplicateFilesSequence);
            if (!hasPatchFilesAction)
            {
                WindowsInstallerStandard.TryGetStandardAction(tableName, "PatchFiles", out var patchFilesActionSymbol);

                var sequence = patchFilesActionSymbol.Sequence;

                // Test for default sequence value's appropriateness
                if (installFilesSequence >= sequence || (0 != duplicateFilesSequence && duplicateFilesSequence <= sequence))
                {
                    if (0 != duplicateFilesSequence)
                    {
                        if (duplicateFilesSequence < installFilesSequence)
                        {
                            throw new WixException(ErrorMessages.InsertInvalidSequenceActionOrder(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action));
                        }
                        else
                        {
                            sequence = (duplicateFilesSequence + installFilesSequence) / 2;
                            if (installFilesSequence == sequence || duplicateFilesSequence == sequence)
                            {
                                throw new WixException(ErrorMessages.InsertSequenceNoSpace(mainFileRow.SourceLineNumbers, tableName, "InstallFiles", "DuplicateFiles", patchFilesActionSymbol.Action));
                            }
                        }
                    }
                    else
                    {
                        sequence = installFilesSequence + 1;
                    }
                }

                var sequenceTable = pairedTransform.EnsureTable(this.TableDefinitions[tableName]);
                if (0 == sequenceTable.Rows.Count)
                {
                    sequenceTable.Operation = TableOperation.Add;
                }

                var patchAction = sequenceTable.CreateRow(null);
                patchAction[0]        = patchFilesActionSymbol.Action;
                patchAction[1]        = patchFilesActionSymbol.Condition;
                patchAction[2]        = sequence;
                patchAction.Operation = RowOperation.Add;
            }
        }
Пример #7
0
        /// <summary>
        /// Creates a work item to create a cabinet.
        /// </summary>
        /// <param name="output">Output for the current database.</param>
        /// <param name="cabinetDir">Directory to create cabinet in.</param>
        /// <param name="mediaSymbol">Media symbol containing information about the cabinet.</param>
        /// <param name="fileFacades">Collection of files in this cabinet.</param>
        /// <returns>created CabinetWorkItem object</returns>
        private CabinetWorkItem CreateCabinetWorkItem(WindowsInstallerData output, string cabinetDir, MediaSymbol mediaSymbol, CompressionLevel compressionLevel, IEnumerable <FileFacade> fileFacades)
        {
            CabinetWorkItem cabinetWorkItem  = null;
            var             tempCabinetFileX = Path.Combine(this.IntermediateFolder, mediaSymbol.Cabinet);

            // check for an empty cabinet
            if (!fileFacades.Any())
            {
                // Remove the leading '#' from the embedded cabinet name to make the warning easier to understand
                var cabinetName = mediaSymbol.Cabinet.TrimStart('#');

                // If building a patch, remind them to run -p for torch.
                if (OutputType.Patch == output.Type)
                {
                    this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName, true));
                }
                else
                {
                    this.Messaging.Write(WarningMessages.EmptyCabinet(mediaSymbol.SourceLineNumbers, cabinetName));
                }
            }

            var cabinetResolver = new CabinetResolver(this.ServiceProvider, this.CabCachePath, this.BackendExtensions);

            var resolvedCabinet = cabinetResolver.ResolveCabinet(tempCabinetFileX, fileFacades);

            // create a cabinet work item if it's not being skipped
            if (CabinetBuildOption.BuildAndCopy == resolvedCabinet.BuildOption || CabinetBuildOption.BuildAndMove == resolvedCabinet.BuildOption)
            {
                // Default to the threshold for best smartcabbing (makes smallest cabinet).
                cabinetWorkItem = new CabinetWorkItem(fileFacades, resolvedCabinet.Path, maxThreshold: 0, compressionLevel, this.ModularizationSuffix /*, this.FileManager*/);
            }
            else // reuse the cabinet from the cabinet cache.
            {
                this.Messaging.Write(VerboseMessages.ReusingCabCache(mediaSymbol.SourceLineNumbers, mediaSymbol.Cabinet, resolvedCabinet.Path));

                try
                {
                    // Ensure the cached cabinet timestamp is current to prevent perpetual incremental builds. The
                    // problematic scenario goes like this. Imagine two cabinets in the cache. Update a file that
                    // goes into one of the cabinets. One cabinet will get rebuilt, the other will be copied from
                    // the cache. Now the file (an input) has a newer timestamp than the reused cabient (an output)
                    // causing the project to look like it perpetually needs a rebuild until all of the reused
                    // cabinets get newer timestamps.
                    File.SetLastWriteTime(resolvedCabinet.Path, DateTime.Now);
                }
                catch (Exception e)
                {
                    this.Messaging.Write(WarningMessages.CannotUpdateCabCache(mediaSymbol.SourceLineNumbers, resolvedCabinet.Path, e.Message));
                }
            }

            var trackResolvedCabinet = this.BackendHelper.TrackFile(resolvedCabinet.Path, TrackedFileType.Intermediate, mediaSymbol.SourceLineNumbers);

            this.trackedFiles.Add(trackResolvedCabinet);

            if (mediaSymbol.Cabinet.StartsWith("#", StringComparison.Ordinal))
            {
                var streamsTable = output.EnsureTable(this.TableDefinitions["_Streams"]);

                var streamRow = streamsTable.CreateRow(mediaSymbol.SourceLineNumbers);
                streamRow[0] = mediaSymbol.Cabinet.Substring(1);
                streamRow[1] = resolvedCabinet.Path;
            }
            else
            {
                var trackDestination = this.BackendHelper.TrackFile(Path.Combine(cabinetDir, mediaSymbol.Cabinet), TrackedFileType.Final, mediaSymbol.SourceLineNumbers);
                this.trackedFiles.Add(trackDestination);

                var transfer = this.BackendHelper.CreateFileTransfer(resolvedCabinet.Path, trackDestination.Path, resolvedCabinet.BuildOption == CabinetBuildOption.BuildAndMove, mediaSymbol.SourceLineNumbers);
                this.fileTransfers.Add(transfer);
            }

            return(cabinetWorkItem);
        }
Пример #8
0
        public void Execute()
        {
            // Create and add substorages for instance transforms.
            var wixInstanceTransformsSymbols = this.Section.Symbols.OfType <WixInstanceTransformsSymbol>();

            if (wixInstanceTransformsSymbols.Any())
            {
                string targetProductCode    = null;
                string targetUpgradeCode    = null;
                string targetProductVersion = null;

                var targetSummaryInformationTable = this.Output.Tables["_SummaryInformation"];
                var targetPropertyTable           = this.Output.Tables["Property"];

                // Get the data from target database
                foreach (var propertyRow in targetPropertyTable.Rows)
                {
                    if ("ProductCode" == (string)propertyRow[0])
                    {
                        targetProductCode = (string)propertyRow[1];
                    }
                    else if ("ProductVersion" == (string)propertyRow[0])
                    {
                        targetProductVersion = (string)propertyRow[1];
                    }
                    else if ("UpgradeCode" == (string)propertyRow[0])
                    {
                        targetUpgradeCode = (string)propertyRow[1];
                    }
                }

                // Index the Instance Component Rows, we'll get the Components rows from the real Component table.
                var targetInstanceComponentTable = this.Section.Symbols.OfType <WixInstanceComponentSymbol>();
                var instanceComponentGuids       = targetInstanceComponentTable.ToDictionary(t => t.Id.Id, t => (ComponentRow)null);

                if (instanceComponentGuids.Any())
                {
                    var targetComponentTable = this.Output.Tables["Component"];
                    foreach (ComponentRow componentRow in targetComponentTable.Rows)
                    {
                        var component = (string)componentRow[0];
                        if (instanceComponentGuids.ContainsKey(component))
                        {
                            instanceComponentGuids[component] = componentRow;
                        }
                    }
                }

                // Generate the instance transforms
                foreach (var instanceSymbol in wixInstanceTransformsSymbols)
                {
                    var instanceId = instanceSymbol.Id.Id;

                    var instanceTransform = new WindowsInstallerData(instanceSymbol.SourceLineNumbers);
                    instanceTransform.Type     = OutputType.Transform;
                    instanceTransform.Codepage = this.Output.Codepage;

                    var    instanceSummaryInformationTable = instanceTransform.EnsureTable(this.TableDefinitions["_SummaryInformation"]);
                    string targetPlatformAndLanguage       = null;

                    foreach (var summaryInformationRow in targetSummaryInformationTable.Rows)
                    {
                        if (7 == (int)summaryInformationRow[0]) // PID_TEMPLATE
                        {
                            targetPlatformAndLanguage = (string)summaryInformationRow[1];
                        }

                        // Copy the row's data to the transform.
                        var copyOfSummaryRow = instanceSummaryInformationTable.CreateRow(summaryInformationRow.SourceLineNumbers);
                        copyOfSummaryRow[0] = summaryInformationRow[0];
                        copyOfSummaryRow[1] = summaryInformationRow[1];
                    }

                    // Modify the appropriate properties.
                    var propertyTable = instanceTransform.EnsureTable(this.TableDefinitions["Property"]);

                    // Change the ProductCode property
                    var productCode = instanceSymbol.ProductCode;
                    if ("*" == productCode)
                    {
                        productCode = this.BackendHelper.CreateGuid();
                    }

                    var productCodeRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers);
                    productCodeRow.Operation          = RowOperation.Modify;
                    productCodeRow.Fields[1].Modified = true;
                    productCodeRow[0] = "ProductCode";
                    productCodeRow[1] = productCode;

                    // Change the instance property
                    var instanceIdRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers);
                    instanceIdRow.Operation          = RowOperation.Modify;
                    instanceIdRow.Fields[1].Modified = true;
                    instanceIdRow[0] = instanceSymbol.PropertyId;
                    instanceIdRow[1] = instanceId;

                    if (!String.IsNullOrEmpty(instanceSymbol.ProductName))
                    {
                        // Change the ProductName property
                        var productNameRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers);
                        productNameRow.Operation          = RowOperation.Modify;
                        productNameRow.Fields[1].Modified = true;
                        productNameRow[0] = "ProductName";
                        productNameRow[1] = instanceSymbol.ProductName;
                    }

                    if (!String.IsNullOrEmpty(instanceSymbol.UpgradeCode))
                    {
                        // Change the UpgradeCode property
                        var upgradeCodeRow = propertyTable.CreateRow(instanceSymbol.SourceLineNumbers);
                        upgradeCodeRow.Operation          = RowOperation.Modify;
                        upgradeCodeRow.Fields[1].Modified = true;
                        upgradeCodeRow[0] = "UpgradeCode";
                        upgradeCodeRow[1] = instanceSymbol.UpgradeCode;

                        // Change the Upgrade table
                        var targetUpgradeTable = this.Output.Tables["Upgrade"];
                        if (null != targetUpgradeTable && 0 <= targetUpgradeTable.Rows.Count)
                        {
                            var upgradeId    = instanceSymbol.UpgradeCode;
                            var upgradeTable = instanceTransform.EnsureTable(this.TableDefinitions["Upgrade"]);
                            foreach (var row in targetUpgradeTable.Rows)
                            {
                                // In case they are upgrading other codes to this new product, leave the ones that don't match the
                                // Product.UpgradeCode intact.
                                if (targetUpgradeCode == (string)row[0])
                                {
                                    var upgradeRow = upgradeTable.CreateRow(row.SourceLineNumbers);
                                    upgradeRow.Operation          = RowOperation.Add;
                                    upgradeRow.Fields[0].Modified = true;
                                    // I was hoping to be able to RowOperation.Modify, but that didn't appear to function.
                                    // upgradeRow.Fields[0].PreviousData = (string)row[0];

                                    // Inserting a new Upgrade record with the updated UpgradeCode
                                    upgradeRow[0] = upgradeId;
                                    upgradeRow[1] = row[1];
                                    upgradeRow[2] = row[2];
                                    upgradeRow[3] = row[3];
                                    upgradeRow[4] = row[4];
                                    upgradeRow[5] = row[5];
                                    upgradeRow[6] = row[6];

                                    // Delete the old row
                                    var upgradeRemoveRow = upgradeTable.CreateRow(row.SourceLineNumbers);
                                    upgradeRemoveRow.Operation = RowOperation.Delete;
                                    upgradeRemoveRow[0]        = row[0];
                                    upgradeRemoveRow[1]        = row[1];
                                    upgradeRemoveRow[2]        = row[2];
                                    upgradeRemoveRow[3]        = row[3];
                                    upgradeRemoveRow[4]        = row[4];
                                    upgradeRemoveRow[5]        = row[5];
                                    upgradeRemoveRow[6]        = row[6];
                                }
                            }
                        }
                    }

                    // If there are instance Components generate new GUIDs for them.
                    if (0 < instanceComponentGuids.Count)
                    {
                        var componentTable = instanceTransform.EnsureTable(this.TableDefinitions["Component"]);
                        foreach (var targetComponentRow in instanceComponentGuids.Values)
                        {
                            var guid = targetComponentRow.Guid;
                            if (!String.IsNullOrEmpty(guid))
                            {
                                var instanceComponentRow = componentTable.CreateRow(targetComponentRow.SourceLineNumbers);
                                instanceComponentRow.Operation          = RowOperation.Modify;
                                instanceComponentRow.Fields[1].Modified = true;
                                instanceComponentRow[0] = targetComponentRow[0];
                                instanceComponentRow[1] = this.BackendHelper.CreateGuid(BindDatabaseCommand.WixComponentGuidNamespace, String.Concat(guid, instanceId));
                                instanceComponentRow[2] = targetComponentRow[2];
                                instanceComponentRow[3] = targetComponentRow[3];
                                instanceComponentRow[4] = targetComponentRow[4];
                                instanceComponentRow[5] = targetComponentRow[5];
                            }
                        }
                    }

                    // Update the summary information
                    var summaryRows = new Dictionary <int, Row>(instanceSummaryInformationTable.Rows.Count);
                    foreach (var row in instanceSummaryInformationTable.Rows)
                    {
                        summaryRows[(int)row[0]] = row;

                        if ((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage == (int)row[0])
                        {
                            row[1] = targetPlatformAndLanguage;
                        }
                        else if ((int)SummaryInformation.Transform.ProductCodes == (int)row[0])
                        {
                            row[1] = String.Concat(targetProductCode, targetProductVersion, ';', productCode, targetProductVersion, ';', targetUpgradeCode);
                        }
                        else if ((int)SummaryInformation.Transform.ValidationFlags == (int)row[0])
                        {
                            row[1] = 0;
                        }
                        else if ((int)SummaryInformation.Transform.Security == (int)row[0])
                        {
                            row[1] = "4";
                        }
                    }

                    if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.UpdatedPlatformAndLanguage))
                    {
                        var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers);
                        summaryRow[0] = (int)SummaryInformation.Transform.UpdatedPlatformAndLanguage;
                        summaryRow[1] = targetPlatformAndLanguage;
                    }
                    else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.ValidationFlags))
                    {
                        var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers);
                        summaryRow[0] = (int)SummaryInformation.Transform.ValidationFlags;
                        summaryRow[1] = "0";
                    }
                    else if (!summaryRows.ContainsKey((int)SummaryInformation.Transform.Security))
                    {
                        var summaryRow = instanceSummaryInformationTable.CreateRow(instanceSymbol.SourceLineNumbers);
                        summaryRow[0] = (int)SummaryInformation.Transform.Security;
                        summaryRow[1] = "4";
                    }

                    this.Output.SubStorages.Add(new SubStorage(instanceId, instanceTransform));
                }
            }
        }
Пример #9
0
        /// <summary>
        /// Creates a transform by diffing two outputs.
        /// </summary>
        /// <param name="targetOutput">The target output.</param>
        /// <param name="updatedOutput">The updated output.</param>
        /// <param name="validationFlags"></param>
        /// <returns>The transform.</returns>
        public WindowsInstallerData Diff(WindowsInstallerData targetOutput, WindowsInstallerData updatedOutput, TransformFlags validationFlags)
        {
            WindowsInstallerData transform = new WindowsInstallerData(null);

            transform.Type            = OutputType.Transform;
            transform.Codepage        = updatedOutput.Codepage;
            this.transformSummaryInfo = new SummaryInformationStreams();

            // compare the codepages
            if (targetOutput.Codepage != updatedOutput.Codepage && 0 == (TransformFlags.ErrorChangeCodePage & validationFlags))
            {
                this.messaging.Write(ErrorMessages.OutputCodepageMismatch(targetOutput.SourceLineNumbers, targetOutput.Codepage, updatedOutput.Codepage));
                if (null != updatedOutput.SourceLineNumbers)
                {
                    this.messaging.Write(ErrorMessages.OutputCodepageMismatch2(updatedOutput.SourceLineNumbers));
                }
            }

            // compare the output types
            if (targetOutput.Type != updatedOutput.Type)
            {
                throw new WixException(ErrorMessages.OutputTypeMismatch(targetOutput.SourceLineNumbers, targetOutput.Type.ToString(), updatedOutput.Type.ToString()));
            }

            // compare the contents of the tables
            foreach (Table targetTable in targetOutput.Tables)
            {
                Table          updatedTable = updatedOutput.Tables[targetTable.Name];
                TableOperation operation    = TableOperation.None;

                List <Row> rows = this.CompareTables(targetOutput, targetTable, updatedTable, out operation);

                if (TableOperation.Drop == operation)
                {
                    Table droppedTable = transform.EnsureTable(targetTable.Definition);
                    droppedTable.Operation = TableOperation.Drop;
                }
                else if (TableOperation.None == operation)
                {
                    Table modified = transform.EnsureTable(updatedTable.Definition);
                    rows.ForEach(r => modified.Rows.Add(r));
                }
            }

            // added tables
            foreach (Table updatedTable in updatedOutput.Tables)
            {
                if (null == targetOutput.Tables[updatedTable.Name])
                {
                    Table addedTable = transform.EnsureTable(updatedTable.Definition);
                    addedTable.Operation = TableOperation.Add;

                    foreach (Row updatedRow in updatedTable.Rows)
                    {
                        updatedRow.Operation = RowOperation.Add;
                        updatedRow.SectionId = sectionDelimiter + updatedRow.SectionId;
                        addedTable.Rows.Add(updatedRow);
                    }
                }
            }

            // set summary information properties
            if (!this.suppressKeepingSpecialRows)
            {
                Table summaryInfoTable = transform.Tables["_SummaryInformation"];
                this.UpdateTransformSummaryInformationTable(summaryInfoTable, validationFlags);
            }

            return(transform);
        }