Пример #1
1
        /// <summary>
        /// Merges in any modules to the output database.
        /// </summary>
        /// <param name="tempDatabaseFile">The temporary database file.</param>
        /// <param name="output">Output that specifies database and modules to merge.</param>
        /// <param name="fileRows">The indexed file rows.</param>
        /// <param name="suppressedTableNames">The names of tables that are suppressed.</param>
        /// <remarks>Expects that output's database has already been generated.</remarks>
        private void MergeModules(string tempDatabaseFile, Output output, FileRowCollection fileRows, StringCollection suppressedTableNames)
        {
            Debug.Assert(OutputType.Product == output.Type);

            Table wixMergeTable = output.Tables["WixMerge"];
            Table wixFeatureModulesTable = output.Tables["WixFeatureModules"];

            // check for merge rows to see if there is any work to do
            if (null == wixMergeTable || 0 == wixMergeTable.Rows.Count)
            {
                return;
            }

            IMsmMerge2 merge = null;
            bool commit = true;
            bool logOpen = false;
            bool databaseOpen = false;
            string logPath = null;
            try
            {
                merge = NativeMethods.GetMsmMerge();

                logPath = Path.Combine(this.TempFilesLocation, "merge.log");
                merge.OpenLog(logPath);
                logOpen = true;

                merge.OpenDatabase(tempDatabaseFile);
                databaseOpen = true;

                // process all the merge rows
                foreach (WixMergeRow wixMergeRow in wixMergeTable.Rows)
                {
                    bool moduleOpen = false;

                    try
                    {
                        short mergeLanguage;

                        try
                        {
                            mergeLanguage = Convert.ToInt16(wixMergeRow.Language, CultureInfo.InvariantCulture);
                        }
                        catch (System.FormatException)
                        {
                            this.core.OnMessage(WixErrors.InvalidMergeLanguage(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.Language));
                            continue;
                        }

                        this.core.OnMessage(WixVerboses.OpeningMergeModule(wixMergeRow.SourceFile, mergeLanguage));
                        merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
                        moduleOpen = true;

                        // If there is merge configuration data, create a callback object to contain it all.
                        ConfigurationCallback callback = null;
                        if (!String.IsNullOrEmpty(wixMergeRow.ConfigurationData))
                        {
                            callback = new ConfigurationCallback(wixMergeRow.ConfigurationData);
                        }

                        // merge the module into the database that's being built
                        this.core.OnMessage(WixVerboses.MergingMergeModule(wixMergeRow.SourceFile));
                        merge.MergeEx(wixMergeRow.Feature, wixMergeRow.Directory, callback);

                        // connect any non-primary features
                        if (null != wixFeatureModulesTable)
                        {
                            foreach (Row row in wixFeatureModulesTable.Rows)
                            {
                                if (wixMergeRow.Id == (string)row[1])
                                {
                                    this.core.OnMessage(WixVerboses.ConnectingMergeModule(wixMergeRow.SourceFile, (string)row[0]));
                                    merge.Connect((string)row[0]);
                                }
                            }
                        }
                    }
                    catch (COMException)
                    {
                        commit = false;
                    }
                    finally
                    {
                        IMsmErrors mergeErrors = merge.Errors;

                        // display all the errors encountered during the merge operations for this module
                        for (int i = 1; i <= mergeErrors.Count; i++)
                        {
                            IMsmError mergeError = mergeErrors[i];
                            StringBuilder databaseKeys = new StringBuilder();
                            StringBuilder moduleKeys = new StringBuilder();

                            // build a string of the database keys
                            for (int j = 1; j <= mergeError.DatabaseKeys.Count; j++)
                            {
                                if (1 != j)
                                {
                                    databaseKeys.Append(';');
                                }
                                databaseKeys.Append(mergeError.DatabaseKeys[j]);
                            }

                            // build a string of the module keys
                            for (int j = 1; j <= mergeError.ModuleKeys.Count; j++)
                            {
                                if (1 != j)
                                {
                                    moduleKeys.Append(';');
                                }
                                moduleKeys.Append(mergeError.ModuleKeys[j]);
                            }

                            // display the merge error based on the msm error type
                            switch (mergeError.Type)
                            {
                                case MsmErrorType.msmErrorExclusion:
                                    this.core.OnMessage(WixErrors.MergeExcludedModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleKeys.ToString()));
                                    break;
                                case MsmErrorType.msmErrorFeatureRequired:
                                    this.core.OnMessage(WixErrors.MergeFeatureRequired(wixMergeRow.SourceLineNumbers, mergeError.ModuleTable, moduleKeys.ToString(), wixMergeRow.SourceFile, wixMergeRow.Id));
                                    break;
                                case MsmErrorType.msmErrorLanguageFailed:
                                    this.core.OnMessage(WixErrors.MergeLanguageFailed(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile));
                                    break;
                                case MsmErrorType.msmErrorLanguageUnsupported:
                                    this.core.OnMessage(WixErrors.MergeLanguageUnsupported(wixMergeRow.SourceLineNumbers, mergeError.Language, wixMergeRow.SourceFile));
                                    break;
                                case MsmErrorType.msmErrorResequenceMerge:
                                    this.core.OnMessage(WixWarnings.MergeRescheduledAction(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile));
                                    break;
                                case MsmErrorType.msmErrorTableMerge:
                                    if ("_Validation" != mergeError.DatabaseTable) // ignore merge errors in the _Validation table
                                    {
                                        this.core.OnMessage(WixWarnings.MergeTableFailed(wixMergeRow.SourceLineNumbers, mergeError.DatabaseTable, databaseKeys.ToString(), wixMergeRow.SourceFile));
                                    }
                                    break;
                                case MsmErrorType.msmErrorPlatformMismatch:
                                    this.core.OnMessage(WixErrors.MergePlatformMismatch(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile));
                                    break;
                                default:
                                    this.core.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorWithType, Enum.GetName(typeof(MsmErrorType), mergeError.Type), logPath), "InvalidOperationException", Environment.StackTrace));
                                    break;
                            }
                        }

                        if (0 >= mergeErrors.Count && !commit)
                        {
                            this.core.OnMessage(WixErrors.UnexpectedException(String.Format(CultureInfo.CurrentUICulture, WixStrings.EXP_UnexpectedMergerErrorInSourceFile, wixMergeRow.SourceFile, logPath), "InvalidOperationException", Environment.StackTrace));
                        }

                        if (moduleOpen)
                        {
                            merge.CloseModule();
                        }
                    }
                }
            }
            finally
            {
                if (databaseOpen)
                {
                    merge.CloseDatabase(commit);
                }

                if (logOpen)
                {
                    merge.CloseLog();
                }
            }

            // stop processing if an error previously occurred
            if (this.core.EncounteredError)
            {
                return;
            }

            using (Database db = new Database(tempDatabaseFile, OpenDatabase.Direct))
            {
                Table suppressActionTable = output.Tables["WixSuppressAction"];

                // suppress individual actions
                if (null != suppressActionTable)
                {
                    foreach (Row row in suppressActionTable.Rows)
                    {
                        if (db.TableExists((string)row[0]))
                        {
                            string query = String.Format(CultureInfo.InvariantCulture, "SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]);

                            using (View view = db.OpenExecuteView(query))
                            {
                                using (Record record = view.Fetch())
                                {
                                    if (null != record)
                                    {
                                        this.core.OnMessage(WixWarnings.SuppressMergedAction((string)row[1], row[0].ToString()));
                                        view.Modify(ModifyView.Delete, record);
                                    }
                                }
                            }
                        }
                    }
                }

                // query for merge module actions in suppressed sequences and drop them
                foreach (string tableName in suppressedTableNames)
                {
                    if (!db.TableExists(tableName))
                    {
                        continue;
                    }

                    using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName)))
                    {
                        while (true)
                        {
                            using (Record resultRecord = view.Fetch())
                            {
                                if (null == resultRecord)
                                {
                                    break;
                                }

                                this.core.OnMessage(WixWarnings.SuppressMergedAction(resultRecord.GetString(1), tableName));
                            }
                        }
                    }

                    // drop suppressed sequences
                    using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName)))
                    {
                    }

                    // delete the validation rows
                    using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?")))
                    {
                        using (Record record = new Record(1))
                        {
                            record.SetString(1, tableName);
                            view.Execute(record);
                        }
                    }
                }

                // now update the Attributes column for the files from the Merge Modules
                this.core.OnMessage(WixVerboses.ResequencingMergeModuleFiles());
                using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?"))
                {
                    foreach (FileRow fileRow in fileRows)
                    {
                        if (!fileRow.FromModule)
                        {
                            continue;
                        }

                        using (Record record = new Record(1))
                        {
                            record.SetString(1, fileRow.File);
                            view.Execute(record);
                        }

                        using (Record recordUpdate = view.Fetch())
                        {
                            if (null == recordUpdate)
                            {
                                throw new InvalidOperationException("Failed to fetch a File row from the database that was merged in from a module.");
                            }

                            recordUpdate.SetInteger(1, fileRow.Sequence);

                            // update the file attributes to match the compression specified
                            // on the Merge element or on the Package element
                            int attributes = 0;

                            // get the current value if its not null
                            if (!recordUpdate.IsNull(2))
                            {
                                attributes = recordUpdate.GetInteger(2);
                            }

                            if (YesNoType.Yes == fileRow.Compressed)
                            {
                                // these are mutually exclusive
                                attributes |= MsiInterop.MsidbFileAttributesCompressed;
                                attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
                            }
                            else if (YesNoType.No == fileRow.Compressed)
                            {
                                // these are mutually exclusive
                                attributes |= MsiInterop.MsidbFileAttributesNoncompressed;
                                attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
                            }
                            else // not specified
                            {
                                Debug.Assert(YesNoType.NotSet == fileRow.Compressed);

                                // clear any compression bits
                                attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
                                attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
                            }
                            recordUpdate.SetInteger(2, attributes);

                            view.Modify(ModifyView.Update, recordUpdate);
                        }
                    }
                }

                db.Commit();
            }
        }
Пример #2
0
        /// <summary>
        /// Creates the MSI/MSM/PCP database.
        /// </summary>
        /// <param name="output">Output to create database for.</param>
        /// <param name="databaseFile">The database file to create.</param>
        /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param>
        /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
        internal void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
        {
            // add the _Validation rows
            if (!this.suppressAddingValidationRows)
            {
                Table validationTable = output.EnsureTable(this.core.TableDefinitions["_Validation"]);

                foreach (Table table in output.Tables)
                {
                    if (!table.Definition.IsUnreal)
                    {
                        // add the validation rows for this table
                        table.Definition.AddValidationRows(validationTable);
                    }
                }
            }

            // set the base directory
            string baseDirectory = this.TempFilesLocation;
            if (useSubdirectory)
            {
                string filename = Path.GetFileNameWithoutExtension(databaseFile);
                baseDirectory = Path.Combine(baseDirectory, filename);

                // make sure the directory exists
                Directory.CreateDirectory(baseDirectory);
            }

            try
            {
                OpenDatabase type = OpenDatabase.CreateDirect;

                // set special flag for patch files
                if (OutputType.Patch == output.Type)
                {
                    type |= OpenDatabase.OpenPatchFile;
                }

                // try to create the database
                using (Database db = new Database(databaseFile, type))
                {
                    // localize the codepage if a value was specified by the localizer
                    if (null != this.Localizer && -1 != this.Localizer.Codepage)
                    {
                        output.Codepage = this.Localizer.Codepage;
                    }

                    // if we're not using the default codepage, import a new one into our
                    // database before we add any tables (or the tables would be added
                    // with the wrong codepage)
                    if (0 != output.Codepage)
                    {
                        this.SetDatabaseCodepage(db, output);
                    }

                    // insert substorages (like transforms inside a patch)
                    if (0 < output.SubStorages.Count)
                    {
                        using (View storagesView = new View(db, "SELECT `Name`, `Data` FROM `_Storages`"))
                        {
                            foreach (SubStorage subStorage in output.SubStorages)
                            {
                                string transformFile = Path.Combine(this.TempFilesLocation, String.Concat(subStorage.Name, ".mst"));

                                // bind the transform
                                if (this.BindTransform(subStorage.Data, transformFile))
                                {
                                    // add the storage
                                    using (Record record = new Record(2))
                                    {
                                        record.SetString(1, subStorage.Name);
                                        record.SetStream(2, transformFile);
                                        storagesView.Modify(ModifyView.Assign, record);
                                    }
                                }
                            }
                        }

                        // some empty transforms may have been excluded
                        // we need to remove these from the final patch summary information
                        if (OutputType.Patch == output.Type && this.AllowEmptyTransforms)
                        {
                            Table patchSummaryInfo = output.EnsureTable(this.core.TableDefinitions["_SummaryInformation"]);
                            for (int i = patchSummaryInfo.Rows.Count - 1; i >= 0; i--)
                            {
                                Row row = patchSummaryInfo.Rows[i];
                                if ((int)SummaryInformation.Patch.ProductCodes == (int)row[0])
                                {
                                    if (nonEmptyProductCodes.Count > 0)
                                    {
                                        string[] productCodes = new string[nonEmptyProductCodes.Count];
                                        nonEmptyProductCodes.CopyTo(productCodes, 0);
                                        row[1] = String.Join(";", productCodes);
                                    }
                                    else
                                    {
                                        row[1] = Binder.NullString;
                                    }
                                }
                                else if ((int)SummaryInformation.Patch.TransformNames == (int)row[0])
                                {
                                    if (nonEmptyTransformNames.Count > 0)
                                    {
                                        string[] transformNames = new string[nonEmptyTransformNames.Count];
                                        nonEmptyTransformNames.CopyTo(transformNames, 0);
                                        row[1] = String.Join(";", transformNames);
                                    }
                                    else
                                    {
                                        row[1] = Binder.NullString;
                                    }
                                }
                            }
                        }
                    }

                    foreach (Table table in output.Tables)
                    {
                        Table importTable = table;
                        bool hasBinaryColumn = false;

                        // skip all unreal tables other than _Streams
                        if (table.Definition.IsUnreal && "_Streams" != table.Name)
                        {
                            continue;
                        }

                        // Do not put the _Validation table in patches, it is not needed
                        if (OutputType.Patch == output.Type && "_Validation" == table.Name)
                        {
                            continue;
                        }

                        // The only way to import binary data is to copy it to a local subdirectory first.
                        // To avoid this extra copying and perf hit, import an empty table with the same
                        // definition and later import the binary data from source using records.
                        foreach (ColumnDefinition columnDefinition in table.Definition.Columns)
                        {
                            if (ColumnType.Object == columnDefinition.Type)
                            {
                                importTable = new Table(table.Section, table.Definition);
                                hasBinaryColumn = true;
                                break;
                            }
                        }

                        // create the table via IDT import
                        if ("_Streams" != importTable.Name)
                        {
                            try
                            {
                                db.ImportTable(output.Codepage, this.core, importTable, baseDirectory, keepAddedColumns);
                            }
                            catch (WixInvalidIdtException)
                            {
                                // If ValidateRows finds anything it doesn't like, it throws
                                importTable.ValidateRows();

                                // Otherwise we rethrow the InvalidIdt
                                throw;
                            }
                        }

                        // insert the rows via SQL query if this table contains object fields
                        if (hasBinaryColumn)
                        {
                            StringBuilder query = new StringBuilder("SELECT ");

                            // build the query for the view
                            bool firstColumn = true;
                            foreach (ColumnDefinition columnDefinition in table.Definition.Columns)
                            {
                                if (!firstColumn)
                                {
                                    query.Append(",");
                                }
                                query.AppendFormat(" `{0}`", columnDefinition.Name);
                                firstColumn = false;
                            }
                            query.AppendFormat(" FROM `{0}`", table.Name);

                            using (View tableView = db.OpenExecuteView(query.ToString()))
                            {
                                // import each row containing a stream
                                foreach (Row row in table.Rows)
                                {
                                    using (Record record = new Record(table.Definition.Columns.Count))
                                    {
                                        StringBuilder streamName = new StringBuilder();
                                        bool needStream = false;

                                        // the _Streams table doesn't prepend the table name (or a period)
                                        if ("_Streams" != table.Name)
                                        {
                                            streamName.Append(table.Name);
                                        }

                                        for (int i = 0; i < table.Definition.Columns.Count; i++)
                                        {
                                            ColumnDefinition columnDefinition = table.Definition.Columns[i];

                                            switch (columnDefinition.Type)
                                            {
                                                case ColumnType.Localized:
                                                case ColumnType.Preserved:
                                                case ColumnType.String:
                                                    if (columnDefinition.IsPrimaryKey)
                                                    {
                                                        if (0 < streamName.Length)
                                                        {
                                                            streamName.Append(".");
                                                        }
                                                        streamName.Append((string)row[i]);
                                                    }

                                                    record.SetString(i + 1, (string)row[i]);
                                                    break;
                                                case ColumnType.Number:
                                                    record.SetInteger(i + 1, Convert.ToInt32(row[i], CultureInfo.InvariantCulture));
                                                    break;
                                                case ColumnType.Object:
                                                    if (null != row[i])
                                                    {
                                                        needStream = true;
                                                        try
                                                        {
                                                            record.SetStream(i + 1, (string)row[i]);
                                                        }
                                                        catch (Win32Exception e)
                                                        {
                                                            if (0xA1 == e.NativeErrorCode) // ERROR_BAD_PATHNAME
                                                            {
                                                                throw new WixException(WixErrors.FileNotFound(row.SourceLineNumbers, (string)row[i]));
                                                            }
                                                            else
                                                            {
                                                                throw new WixException(WixErrors.Win32Exception(e.NativeErrorCode, e.Message));
                                                            }
                                                        }
                                                    }
                                                    break;
                                            }
                                        }

                                        // stream names are created by concatenating the name of the table with the values
                                        // of the primary key (delimited by periods)
                                        // check for a stream name that is more than 62 characters long (the maximum allowed length)
                                        if (needStream && MsiInterop.MsiMaxStreamNameLength < streamName.Length)
                                        {
                                            this.core.OnMessage(WixErrors.StreamNameTooLong(row.SourceLineNumbers, table.Name, streamName.ToString(), streamName.Length));
                                        }
                                        else // add the row to the database
                                        {
                                            tableView.Modify(ModifyView.Assign, record);
                                        }
                                    }
                                }
                            }

                            // Remove rows from the _Streams table for wixpdbs.
                            if ("_Streams" == table.Name)
                            {
                                table.Rows.Clear();
                            }
                        }
                    }

                    // we're good, commit the changes to the new MSI
                    db.Commit();
                }
            }
            catch (IOException)
            {
                // TODO: this error message doesn't seem specific enough
                throw new WixFileNotFoundException(SourceLineNumberCollection.FromFileName(databaseFile), databaseFile);
            }
        }
Пример #3
0
        /// <summary>
        /// Processes the Dialog table.
        /// </summary>
        /// <param name="parentWriter">XmlWriter where the Intermediate should persist itself as XML.</param>
        private void ProcessDialogTable(XmlWriter parentWriter)
        {
            const string tableName = "Dialog";
            if (!this.inputDatabase.TableExists(tableName))
            {
                return;
            }

            using (View view = this.inputDatabase.OpenExecuteView(String.Concat("SELECT * FROM `", tableName, "`")))
            {
                Record record;
                while (view.Fetch(out record))
                {
                    XmlTextWriter writer = this.InitializeXmlTextWriter(Path.GetFullPath(Path.Combine(this.outputFolder, String.Concat("SkuLibrary\\UI\\Dialogs\\", record[(int)MsiInterop.Dialog.Dialog], ".wxs"))));
                    if (this.generateFragments)
                    {
                        // initalize the fragment
                        writer.WriteStartElement("Fragment");
                        this.core.WriteAttributeString(writer, "Id", record[(int)MsiInterop.Dialog.Dialog]);

                        // initalize the UI element
                        writer.WriteStartElement("UI");

                        // create reference to fragment
                        parentWriter.WriteStartElement("FragmentRef");
                        this.core.WriteAttributeString(parentWriter, "Id", record[(int)MsiInterop.Dialog.Dialog]);
                        parentWriter.WriteEndElement();
                    }

                    // TODO: test this for blowing up when null, error violation of SDK constraint "This must be a non-negative number"
                    int bits = Convert.ToInt32(record[(int)MsiInterop.Dialog.Attributes]) ^ MsiInterop.DialogAttributesInvert; // nullable field
                    string firstControl = record[(int)MsiInterop.Dialog.ControlFirst];
                    string defaultControl = record[(int)MsiInterop.Dialog.ControlDefault];
                    string cancelControl  = record[(int)MsiInterop.Dialog.ControlCancel];
                    string str;

                    writer.WriteStartElement(tableName);
                    this.core.WriteAttributeString(writer, "Id", record[(int)MsiInterop.Dialog.Dialog]);

                    if (50 != Convert.ToInt32(record[(int)MsiInterop.Dialog.HCentering])) // non-nullable field
                    {
                        this.core.WriteAttributeString(writer, "X", record[(int)MsiInterop.Dialog.HCentering]);
                    }

                    if (50 != Convert.ToInt32(record[(int)MsiInterop.Dialog.VCentering])) // non-nullable field
                    {
                        this.core.WriteAttributeString(writer, "Y", record[(int)MsiInterop.Dialog.VCentering]);
                    }

                    this.core.WriteAttributeString(writer, "Width", record[(int)MsiInterop.Dialog.Width]);
                    this.core.WriteAttributeString(writer, "Height", record[(int)MsiInterop.Dialog.Height]);
                    str = record[(int)MsiInterop.Dialog.Title];
                    this.core.WriteAttributeString(writer, "Title", str);

                    if (0 < (bits & MsiInterop.MsidbDialogAttributesModal))
                    {
                        bits = bits & ~MsiInterop.MsidbDialogAttributesMinimize;
                    }

                    if (0 < (bits & MsiInterop.MsidbDialogAttributesError))
                    {
                        this.core.WriteAttributeString(writer, "ErrorDialog", "yes"); // can we discover this ourselves?
                    }

                    for (int index = 0; index <= MsiInterop.DialogAttributes.Length; index++)
                    {
                        if (0 < (bits & 1))
                        {
                            string name = MsiInterop.DialogAttributes[index];
                            if (null == name || 0 == name.Length)
                            {
                                // TODO: throw an exception - Fail "Unknown attribute at bit position " & index
                            }
                            this.core.WriteAttributeString(writer, name, "yes");
                        }
                        bits = bits / 2;
                    }

                    Record sqlParams = new Record(2);
                    if (this.inputDatabase.TableExists("Control"))
                    {
                        using (View dialogView = this.inputDatabase.OpenView("SELECT * FROM `Control` WHERE `Dialog_` = ?"))
                        {
                            ArrayList processedControls = new ArrayList();
                            using (View dialogControlView = this.inputDatabase.OpenView("SELECT * FROM `Control` WHERE `Dialog_` = ? AND `Control` = ?"))
                            {
                                string nextControl = "_WiX_First_Control_Never_Matches_Anything_";
                                sqlParams.SetString(1, record[(int)MsiInterop.Dialog.Dialog]);

                                while (0 < nextControl.Length && nextControl != firstControl)
                                {
                                    if ("_WiX_First_Control_Never_Matches_Anything_" == nextControl)
                                    {
                                        nextControl = firstControl;
                                    }

                                    sqlParams.SetString(2, nextControl);
                                    dialogControlView.Execute(sqlParams);
                                    Record dialogControlRecord;
                                    dialogControlView.Fetch(out dialogControlRecord);
                                    if (null == dialogControlRecord)
                                    {
                                        // TODO: throw an exception - Fail "Control " & nextControl & " not found"
                                        break;
                                    }

                                    this.ProcessControl(writer, parentWriter, dialogControlRecord, defaultControl, cancelControl, false);
                                    processedControls.Add(nextControl);
                                    nextControl = dialogControlRecord[(int)MsiInterop.Control.ControlNext];
                                }
                            }

                            dialogView.Execute(sqlParams);
                            Record dialogRecord;
                            while (dialogView.Fetch(out dialogRecord))
                            {
                                string nextControl = dialogRecord[(int)MsiInterop.Control.Control];
                                if (null != nextControl && nextControl != firstControl && !processedControls.Contains(nextControl))
                                {
                                    this.ProcessControl(writer, parentWriter, dialogRecord, defaultControl, cancelControl, true);
                                }
                            }
                        }
                    }

                    if (this.generateFragments)
                    {
                        // terminate the UI element
                        writer.WriteEndElement();

                        // terminate the fragment
                        writer.WriteEndElement();
                    }
                    writer.WriteEndElement();
                }
            }
        }
Пример #4
0
        /// <summary>
        /// Process control.
        /// </summary>
        /// <param name="writer">XmlWriter where the Intermediate should persist itself as XML.</param>
        /// <param name="parentWriter">XmlWriter where the Intermediate should persist itself as XML.</param>
        /// <param name="record">Record from Control table.</param>
        /// <param name="defaultControl">Name of default control.</param>
        /// <param name="cancelControl">Name of cancel control.</param>
        /// <param name="tabDisabled">Specifies if control should not be tabbed to.</param>
        private void ProcessControl(XmlWriter writer, XmlWriter parentWriter, Record record, string defaultControl, string cancelControl, bool tabDisabled)
        {
            const string tableName = "Control";
            const string tableNameDependent = "CheckBox";
            bool hasCheckBoxTable = false;
            if (!this.inputDatabase.TableExists(tableName))
            {
                return;
            }

            if (this.inputDatabase.TableExists(tableNameDependent))
            {
                hasCheckBoxTable = true;
            }

            string id = record[(int)MsiInterop.Control.Control];
            string controlType = record[(int)MsiInterop.Control.Type];

            string text = record[(int)MsiInterop.Control.Text];
            if (!(this.skipInstallShield && 0 < text.IndexOf("InstallShield")))
            {
                writer.WriteStartElement(tableName);
                this.core.WriteAttributeString(writer, "Id", id);
                this.core.WriteAttributeString(writer, "Type", controlType);
                this.core.WriteAttributeString(writer, "X", record[(int)MsiInterop.Control.X]);
                this.core.WriteAttributeString(writer, "Y", record[(int)MsiInterop.Control.Y]);
                this.core.WriteAttributeString(writer, "Width", record[(int)MsiInterop.Control.Width]);
                this.core.WriteAttributeString(writer, "Height", record[(int)MsiInterop.Control.Height]);
                this.core.WriteAttributeString(writer, "Property", record[(int)MsiInterop.Control.Property]);
                string[] helpArray = record[(int)MsiInterop.Control.Help].Split('|');
                if (0 < helpArray.Length)
                {
                    this.core.WriteAttributeString(writer, "ToolTip", helpArray[0]);
                    if (1 < helpArray.Length && 0 < helpArray[1].Length)
                    {
                        this.core.WriteAttributeString(writer, "Help", helpArray[1]);
                    }
                }

                if (hasCheckBoxTable && "CheckBox" == controlType)
                {
                    using (View checkBoxView = this.inputDatabase.OpenExecuteView(String.Concat("SELECT `Value` FROM `", tableNameDependent, "` WHERE `Property`='", record[(int)MsiInterop.Control.Property], "'")))
                    {
                        Record checkBoxRecord;
                        while (checkBoxView.Fetch(out checkBoxRecord)) // will return a single value or will fail
                        {
                            this.core.WriteAttributeString(writer, "CheckBoxValue", checkBoxRecord[1]); // hard coded value because select (above) is for a single column
                        }
                    }
                }

                if (id == defaultControl)
                {
                    this.core.WriteAttributeString(writer, "Default", "yes");
                }

                if (id == cancelControl)
                {
                    this.core.WriteAttributeString(writer, "Cancel", "yes");
                }

                // TODO: test Attributes for blowing up when null, error violation of SDK constraint "This must be a non-negative number"
                this.ProcessControlAttributes(writer, controlType, Convert.ToInt32(record[(int)MsiInterop.Control.Attributes]), tabDisabled);

                if (0 < record[(int)MsiInterop.Control.Text].Length)
                {
                    if (20 >= text.Length && !this.NeedsEscape(text))
                    {
                        this.core.WriteAttributeString(writer, "Text", text);
                    }
                    else
                    {
                        writer.WriteStartElement("Text");
                        writer.WriteCData(text);
                        writer.WriteEndElement();
                    }
                }

                string property = record[(int)MsiInterop.Control.Property];
                if (0 < property.Length)
                {
                    if ("ListBox" == controlType)
                    {
                        this.ProcessControlGroupTable(writer, "ListBox", record[(int)MsiInterop.Control.Property]);
                    }
                    else if ("RadioButtonGroup" == controlType)
                    {
                        this.ProcessControlGroupTable(writer, "RadioButton", record[(int)MsiInterop.Control.Property]);
                    }
                    else if ("ListView" == controlType)
                    {
                        this.ProcessControlGroupTable(writer, "ListView", record[(int)MsiInterop.Control.Property]);
                    }
                    else if ("ComboBox" == controlType)
                    {
                        this.ProcessControlGroupTable(writer, "ComboBox", record[(int)MsiInterop.Control.Property]);
                    }
                    else
                    {
                        this.EmitProperty(parentWriter, this.nonuiproperties, property);
                    }
                }
                /* broken and not needed right now
                            if ("Icon" == controlType && 0 < record[(int)MsiInterop.Control.Text].Length)
                            {
                                this.ProcessBinaryTable(writer, BinaryType.Binary, record[(int)MsiInterop.Control.Text]);
                            }
                */
                Record sqlParams = new Record(2);
                sqlParams.SetString(1, record[(int)MsiInterop.Control.Dialog]);
                sqlParams.SetString(2, record[(int)MsiInterop.Control.Control]);
                if (this.inputDatabase.TableExists("ControlEvent"))
                {
                    using (View publishView = this.inputDatabase.OpenView("SELECT * FROM `ControlEvent` WHERE `Dialog_` = ? AND `Control_` = ? ORDER BY `Ordering`"))
                    {
                        publishView.Execute(sqlParams);
                        Record publishRecord;
                        while (publishView.Fetch(out publishRecord))
                        {
                            writer.WriteStartElement("Publish");
                            string controlEvent = publishRecord[(int)MsiInterop.ControlEvent.Event];
                            if (controlEvent.StartsWith("[") && controlEvent.EndsWith("]"))
                            {
                                this.core.WriteAttributeString(writer, "Property", controlEvent.Substring(1, controlEvent.Length - 2));
                            }
                            else
                            {
                                this.core.WriteAttributeString(writer, "Event", controlEvent);
                            }
                            this.core.WriteAttributeString(writer, "Value", publishRecord[(int)MsiInterop.ControlEvent.Argument]);
                            writer.WriteCData(publishRecord[(int)MsiInterop.ControlEvent.Condition]);
                            writer.WriteEndElement();
                        }
                    }
                }

                if (this.inputDatabase.TableExists("EventMapping"))
                {
                    using (View subscribeView = this.inputDatabase.OpenView("SELECT * FROM `EventMapping` WHERE `Dialog_` = ? AND `Control_` = ?"))
                    {
                        subscribeView.Execute(sqlParams);
                        Record subscribeRecord;
                        while (subscribeView.Fetch(out subscribeRecord))
                        {
                            writer.WriteStartElement("Subscribe");
                            this.core.WriteAttributeString(writer, "Event", subscribeRecord[(int)MsiInterop.EventMapping.Event]);
                            this.core.WriteAttributeString(writer, "Attribute", subscribeRecord[(int)MsiInterop.EventMapping.Attribute]);
                            writer.WriteEndElement();
                        }
                    }
                }

                if (this.inputDatabase.TableExists("ControlCondition"))
                {
                    using (View conditionView = this.inputDatabase.OpenView("SELECT * FROM `ControlCondition` WHERE `Dialog_` = ? AND `Control_` = ?"))
                    {
                        conditionView.Execute(sqlParams);
                        Record conditionRecord;
                        while (conditionView.Fetch(out conditionRecord))
                        {
                            writer.WriteStartElement("Condition");
                            this.core.WriteAttributeString(writer, "Action", conditionRecord[(int)MsiInterop.ControlCondition.Action].ToLower());
                            writer.WriteCData(conditionRecord[(int)MsiInterop.ControlCondition.Condition]);
                            writer.WriteEndElement();
                        }
                    }
                }
                writer.WriteEndElement();
            }
            else
            {
                this.core.OnMessage(WixWarnings.FilteringInstallShieldStuff(null, WarningLevel.Major, "Control", String.Concat(record[(int)MsiInterop.Control.Dialog], "::", record[(int)MsiInterop.Control.Control])));
            }
        }
Пример #5
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>
        /// <param name="skipSummaryInfo">Option to skip unbinding the _SummaryInformation table.</param>
        /// <returns>The output representing the database.</returns>
        private Output UnbindDatabase(string databaseFile, Database database, OutputType outputType, string exportBasePath, bool skipSummaryInfo)
        {
            string modularizationGuid = null;
            Output output = new Output(SourceLineNumberCollection.FromFileName(databaseFile));
            View validationView = null;

            // set the output type
            output.Type = outputType;

            // get the codepage
            database.Export("_ForceCodepage", this.TempFilesLocation, "_ForceCodepage.idt");
            using (StreamReader sr = File.OpenText(Path.Combine(this.TempFilesLocation, "_ForceCodepage.idt")))
            {
                string line;

                while (null != (line = sr.ReadLine()))
                {
                    string[] data = line.Split('\t');

                    if (2 == data.Length)
                    {
                        output.Codepage = Convert.ToInt32(data[0], CultureInfo.InvariantCulture);
                    }
                }
            }

            // get the summary information table if it exists; it won't if unbinding a transform
            if (!skipSummaryInfo)
            {
                using (SummaryInformation summaryInformation = new SummaryInformation(database))
                {
                    Table table = new Table(null, this.tableDefinitions["_SummaryInformation"]);

                    for (int i = 1; 19 >= i; i++)
                    {
                        string value = summaryInformation.GetProperty(i);

                        if (0 < value.Length)
                        {
                            Row row = table.CreateRow(output.SourceLineNumbers);
                            row[0] = i;
                            row[1] = value;
                        }
                    }

                    output.Tables.Add(table);
                }
            }

            try
            {
                // open a view on the validation table if it exists
                if (database.TableExists("_Validation"))
                {
                    validationView = database.OpenView("SELECT * FROM `_Validation` WHERE `Table` = ? AND `Column` = ?");
                }

                // get the normal tables
                using (View tablesView = database.OpenExecuteView("SELECT * FROM _Tables"))
                {
                    while (true)
                    {
                        using (Record tableRecord = tablesView.Fetch())
                        {
                            if (null == tableRecord)
                            {
                                break;
                            }

                            string tableName = tableRecord.GetString(1);

                            using (View tableView = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName)))
                            {
                                TableDefinition tableDefinition = new TableDefinition(tableName, false, false);
                                Hashtable tablePrimaryKeys = new Hashtable();

                                using (Record columnNameRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFONAMES),
                                              columnTypeRecord = tableView.GetColumnInfo(MsiInterop.MSICOLINFOTYPES))
                                {
                                    int columnCount = columnNameRecord.GetFieldCount();

                                    // index the primary keys
                                    using (Record primaryKeysRecord = database.PrimaryKeys(tableName))
                                    {
                                        int primaryKeysFieldCount = primaryKeysRecord.GetFieldCount();

                                        for (int i = 1; i <= primaryKeysFieldCount; i++)
                                        {
                                            tablePrimaryKeys[primaryKeysRecord.GetString(i)] = null;
                                        }
                                    }

                                    for (int i = 1; i <= columnCount; i++)
                                    {
                                        string columnName = columnNameRecord.GetString(i);
                                        string idtType = columnTypeRecord.GetString(i);

                                        ColumnType columnType;
                                        int length;
                                        bool nullable;

                                        ColumnCategory columnCategory = ColumnCategory.Unknown;
                                        ColumnModularizeType columnModularizeType = ColumnModularizeType.None;
                                        bool primary = tablePrimaryKeys.Contains(columnName);
                                        bool minValueSet = false;
                                        int minValue = -1;
                                        bool maxValueSet = false;
                                        int maxValue = -1;
                                        string keyTable = null;
                                        bool keyColumnSet = false;
                                        int keyColumn = -1;
                                        string category = null;
                                        string set = null;
                                        string description = null;

                                        // get the column type, length, and whether its nullable
                                        switch (Char.ToLower(idtType[0], CultureInfo.InvariantCulture))
                                        {
                                            case 'i':
                                                columnType = ColumnType.Number;
                                                break;
                                            case 'l':
                                                columnType = ColumnType.Localized;
                                                break;
                                            case 's':
                                                columnType = ColumnType.String;
                                                break;
                                            case 'v':
                                                columnType = ColumnType.Object;
                                                break;
                                            default:
                                                // TODO: error
                                                columnType = ColumnType.Unknown;
                                                break;
                                        }
                                        length = Convert.ToInt32(idtType.Substring(1), CultureInfo.InvariantCulture);
                                        nullable = Char.IsUpper(idtType[0]);

                                        // try to get validation information
                                        if (null != validationView)
                                        {
                                            using (Record validationRecord = new Record(2))
                                            {
                                                validationRecord.SetString(1, tableName);
                                                validationRecord.SetString(2, columnName);

                                                validationView.Execute(validationRecord);
                                            }

                                            using (Record validationRecord = validationView.Fetch())
                                            {
                                                if (null != validationRecord)
                                                {
                                                    string validationNullable = validationRecord.GetString(3);
                                                    minValueSet = !validationRecord.IsNull(4);
                                                    minValue = (minValueSet ? validationRecord.GetInteger(4) : -1);
                                                    maxValueSet = !validationRecord.IsNull(5);
                                                    maxValue = (maxValueSet ? validationRecord.GetInteger(5) : -1);
                                                    keyTable = (!validationRecord.IsNull(6) ? validationRecord.GetString(6) : null);
                                                    keyColumnSet = !validationRecord.IsNull(7);
                                                    keyColumn = (keyColumnSet ? validationRecord.GetInteger(7) : -1);
                                                    category = (!validationRecord.IsNull(8) ? validationRecord.GetString(8) : null);
                                                    set = (!validationRecord.IsNull(9) ? validationRecord.GetString(9) : null);
                                                    description = (!validationRecord.IsNull(10) ? validationRecord.GetString(10) : null);

                                                    // check the validation nullable value against the column definition
                                                    if (null == validationNullable)
                                                    {
                                                        // TODO: warn for illegal validation nullable column
                                                    }
                                                    else if ((nullable && "Y" != validationNullable) || (!nullable && "N" != validationNullable))
                                                    {
                                                        // TODO: warn for mismatch between column definition and validation nullable
                                                    }

                                                    // convert category to ColumnCategory
                                                    if (null != category)
                                                    {
                                                        try
                                                        {
                                                            columnCategory = (ColumnCategory)Enum.Parse(typeof(ColumnCategory), category, true);
                                                        }
                                                        catch (ArgumentException)
                                                        {
                                                            columnCategory = ColumnCategory.Unknown;
                                                        }
                                                    }
                                                }
                                                else
                                                {
                                                    // TODO: warn about no validation information
                                                }
                                            }
                                        }

                                        // guess the modularization type
                                        if ("Icon" == keyTable && 1 == keyColumn)
                                        {
                                            columnModularizeType = ColumnModularizeType.Icon;
                                        }
                                        else if ("Condition" == columnName)
                                        {
                                            columnModularizeType = ColumnModularizeType.Condition;
                                        }
                                        else if (ColumnCategory.Formatted == columnCategory || ColumnCategory.FormattedSDDLText == columnCategory)
                                        {
                                            columnModularizeType = ColumnModularizeType.Property;
                                        }
                                        else if (ColumnCategory.Identifier == columnCategory)
                                        {
                                            columnModularizeType = ColumnModularizeType.Column;
                                        }

                                        tableDefinition.Columns.Add(new ColumnDefinition(columnName, columnType, length, primary, nullable, columnModularizeType, (ColumnType.Localized == columnType), minValueSet, minValue, maxValueSet, maxValue, keyTable, keyColumnSet, keyColumn, columnCategory, set, description, true, true));
                                    }
                                }
                                // use our table definitions if core properties are the same; this allows us to take advantage
                                // of wix concepts like localizable columns which current code assumes
                                if (this.tableDefinitions.Contains(tableName) && 0 == tableDefinition.CompareTo(this.tableDefinitions[tableName]))
                                {
                                    tableDefinition = this.tableDefinitions[tableName];
                                }
                                Table table = new Table(null, tableDefinition);

                                while (true)
                                {
                                    using (Record rowRecord = tableView.Fetch())
                                    {
                                        if (null == rowRecord)
                                        {
                                            break;
                                        }

                                        int recordCount = rowRecord.GetFieldCount();
                                        Row row = table.CreateRow(output.SourceLineNumbers);

                                        for (int i = 0; recordCount > i && row.Fields.Length > i; i++)
                                        {
                                            if (rowRecord.IsNull(i + 1))
                                            {
                                                if (!row.Fields[i].Column.IsNullable)
                                                {
                                                    // TODO: display an error for a null value in a non-nullable field OR
                                                    // display a warning and put an empty string in the value to let the compiler handle it
                                                    // (the second option is risky because the later code may make certain assumptions about
                                                    // the contents of a row value)
                                                }
                                            }
                                            else
                                            {
                                                switch (row.Fields[i].Column.Type)
                                                {
                                                    case ColumnType.Number:
                                                        bool success = false;
                                                        int intValue = rowRecord.GetInteger(i + 1);
                                                        if (row.Fields[i].Column.IsLocalizable)
                                                        {
                                                            success = row.BestEffortSetField(i, Convert.ToString(intValue, CultureInfo.InvariantCulture));
                                                        }
                                                        else
                                                        {
                                                            success = row.BestEffortSetField(i, intValue);
                                                        }

                                                        if (!success)
                                                        {
                                                            this.OnMessage(WixWarnings.BadColumnDataIgnored(row.SourceLineNumbers, Convert.ToString(intValue, CultureInfo.InvariantCulture), tableName, row.Fields[i].Column.Name));
                                                        }
                                                        break;
                                                    case ColumnType.Object:
                                                        string sourceFile = "FILE NOT EXPORTED, USE THE dark.exe -x OPTION TO EXPORT BINARIES";

                                                        if (null != exportBasePath)
                                                        {
                                                            string relativeSourceFile = Path.Combine(tableName, row.GetPrimaryKey('.'));
                                                            sourceFile = Path.Combine(exportBasePath, relativeSourceFile);

                                                            // ensure the parent directory exists
                                                            System.IO.Directory.CreateDirectory(Path.Combine(exportBasePath, tableName));

                                                            using (FileStream fs = System.IO.File.Create(sourceFile))
                                                            {
                                                                int bytesRead;
                                                                byte[] buffer = new byte[512];

                                                                while (0 != (bytesRead = rowRecord.GetStream(i + 1, buffer, buffer.Length)))
                                                                {
                                                                    fs.Write(buffer, 0, bytesRead);
                                                                }
                                                            }
                                                        }

                                                        row[i] = sourceFile;
                                                        break;
                                                    default:
                                                        string value = rowRecord.GetString(i + 1);

                                                        switch (row.Fields[i].Column.Category)
                                                        {
                                                            case ColumnCategory.Guid:
                                                                value = value.ToUpper(CultureInfo.InvariantCulture);
                                                                break;
                                                        }

                                                        // de-modularize
                                                        if (!this.suppressDemodularization && OutputType.Module == output.Type && ColumnModularizeType.None != row.Fields[i].Column.ModularizeType)
                                                        {
                                                            Regex modularization = new Regex(@"\.[0-9A-Fa-f]{8}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{4}_[0-9A-Fa-f]{12}");

                                                            if (null == modularizationGuid)
                                                            {
                                                                Match match = modularization.Match(value);
                                                                if (match.Success)
                                                                {
                                                                    modularizationGuid = String.Concat('{', match.Value.Substring(1).Replace('_', '-'), '}');
                                                                }
                                                            }

                                                            value = modularization.Replace(value, String.Empty);
                                                        }

                                                        // escape "$(" for the preprocessor
                                                        value = value.Replace("$(", "$$(");

                                                        // escape things that look like wix variables
                                                        MatchCollection matches = Common.WixVariableRegex.Matches(value);
                                                        for (int j = matches.Count - 1; 0 <= j; j--)
                                                        {
                                                            value = value.Insert(matches[j].Index, "!");
                                                        }

                                                        row[i] = value;
                                                        break;
                                                }
                                            }
                                        }
                                    }
                                }

                                output.Tables.Add(table);
                            }

                        }
                    }
                }
            }
            finally
            {
                if (null != validationView)
                {
                    validationView.Close();
                }
            }

            // set the modularization guid as the PackageCode
            if (null != modularizationGuid)
            {
                Table table = output.Tables["_SummaryInformation"];

                foreach (Row row in table.Rows)
                {
                    if (9 == (int)row[0]) // PID_REVNUMBER
                    {
                        row[1] = modularizationGuid;
                    }
                }
            }

            if (this.isAdminImage)
            {
                GenerateWixFileTable(databaseFile, output);
                GenerateSectionIds(output);
            }

            return output;
        }
Пример #6
0
        /// <summary>
        /// Extract the cabinets from a database.
        /// </summary>
        /// <param name="output">The output to use when finding cabinets.</param>
        /// <param name="database">The database containing the cabinets.</param>
        /// <param name="databaseFile">The location of the database file.</param>
        /// <param name="exportBasePath">The path where the files should be exported.</param>
        private void ExtractCabinets(Output output, Database database, string databaseFile, string exportBasePath)
        {
            string databaseBasePath = Path.GetDirectoryName(databaseFile);
            StringCollection cabinetFiles = new StringCollection();
            SortedList embeddedCabinets = new SortedList();

            // index all of the cabinet files
            if (OutputType.Module == output.Type)
            {
                embeddedCabinets.Add(0, "MergeModule.CABinet");
            }
            else if (null != output.Tables["Media"])
            {
                foreach (MediaRow mediaRow in output.Tables["Media"].Rows)
                {
                    if (null != mediaRow.Cabinet)
                    {
                        if (OutputType.Product == output.Type ||
                            (OutputType.Transform == output.Type && RowOperation.Add == mediaRow.Operation))
                        {
                            if (mediaRow.Cabinet.StartsWith("#", StringComparison.Ordinal))
                            {
                                embeddedCabinets.Add(mediaRow.DiskId, mediaRow.Cabinet.Substring(1));
                            }
                            else
                            {
                                cabinetFiles.Add(Path.Combine(databaseBasePath, mediaRow.Cabinet));
                            }
                        }
                    }
                }
            }

            // extract the embedded cabinet files from the database
            if (0 < embeddedCabinets.Count)
            {
                using (View streamsView = database.OpenView("SELECT `Data` FROM `_Streams` WHERE `Name` = ?"))
                {
                    foreach (int diskId in embeddedCabinets.Keys)
                    {
                        using(Record record = new Record(1))
                        {
                            record.SetString(1, (string)embeddedCabinets[diskId]);
                            streamsView.Execute(record);
                        }

                        using (Record record = streamsView.Fetch())
                        {
                            if (null != record)
                            {
                                // since the cabinets are stored in case-sensitive streams inside the msi, but the file system is not case-sensitive,
                                // embedded cabinets must be extracted to a canonical file name (like their diskid) to ensure extraction will always work
                                string cabinetFile = Path.Combine(this.TempFilesLocation, String.Concat("Media", Path.DirectorySeparatorChar, diskId.ToString(CultureInfo.InvariantCulture), ".cab"));

                                // ensure the parent directory exists
                                System.IO.Directory.CreateDirectory(Path.GetDirectoryName(cabinetFile));

                                using (FileStream fs = System.IO.File.Create(cabinetFile))
                                {
                                    int bytesRead;
                                    byte[] buffer = new byte[512];

                                    while (0 != (bytesRead = record.GetStream(1, buffer, buffer.Length)))
                                    {
                                        fs.Write(buffer, 0, bytesRead);
                                    }
                                }

                                cabinetFiles.Add(cabinetFile);
                            }
                            else
                            {
                                // TODO: warning about missing embedded cabinet
                            }
                        }
                    }
                }
            }

            // extract the cabinet files
            if (0 < cabinetFiles.Count)
            {
                string fileDirectory = Path.Combine(exportBasePath, "File");

                // delete the directory and its files to prevent cab extraction due to an existing file
                if (Directory.Exists(fileDirectory))
                {
                    Directory.Delete(fileDirectory, true);
                }

                // ensure the directory exists or extraction will fail
                Directory.CreateDirectory(fileDirectory);

                foreach (string cabinetFile in cabinetFiles)
                {
                    using (WixExtractCab extractCab = new WixExtractCab())
                    {
                        try
                        {
                            extractCab.Extract(cabinetFile, fileDirectory);
                        }
                        catch (FileNotFoundException)
                        {
                            throw new WixException(WixErrors.FileNotFound(SourceLineNumberCollection.FromFileName(databaseFile), cabinetFile));
                        }
                    }
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Merges in any modules to the output database.
        /// </summary>
        /// <param name="databasePath">Path to database.</param>
        /// <param name="output">Output that specifies database and modules to merge.</param>
        /// <remarks>Expects that output's database has already been generated.</remarks>
        private void MergeModules(string databasePath, Output output)
        {
            Debug.Assert(OutputType.Product == output.Type);

            if (0 == output.Modules.Count)   // no work to do
            {
                return;
            }

            IMsmMerge2 merge = null;
            bool commit = false;
            bool logOpen = false;
            bool databaseOpen = false;
            bool moduleOpen = false;
            try
            {
                bool foundError = false;
                MsmMerge msm = new MsmMerge();
                merge = (IMsmMerge2)msm;

                merge.OpenLog(String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, "merge.log"));
                logOpen = true;

                merge.OpenDatabase(databasePath);
                databaseOpen = true;

                // process all the merge rows
                foreach (MergeRow mergeRow in output.Modules)
                {
                    string mergeModulePath = null;
                    try
                    {
                        mergeModulePath = this.extension.FileResolutionHandler(mergeRow.SourceFile, FileResolutionType.Module);
                    }
                    catch (WixFileNotFoundException wfnfe)
                    {
                        this.OnMessage(WixErrors.BinderExtensionMissingFile(mergeRow.SourceLineNumbers, ErrorLevel.Normal, wfnfe.Message));
                        foundError = true;
                        continue;
                    }

                    try
                    {
                        merge.OpenModule(mergeModulePath, mergeRow.Language);
                    }
                    catch (COMException ce)
                    {
                        if (-2147023273 == ce.ErrorCode) // 0x80070657 - ERROR_INSTALL_LANGUAGE_UNSUPPORTED
                        {
                            throw new WixUnknownMergeLanguageException(mergeRow.SourceLineNumbers, mergeRow.Id, mergeModulePath, mergeRow.Language, ce);
                        }
                        else
                        {
                            throw;
                        }
                    }
                    moduleOpen = true;

                    ConnectToFeature connection = output.ModulesToFeatures[mergeRow.Id];
                    if (null == connection)
                    {
                        throw new WixMergeModuleMissingFeatureException(mergeRow.SourceLineNumbers, mergeRow.Id);
                    }

                    string configData = mergeRow.ConfigurationData;
                    if (null != configData)
                    {
                        ConfigurationCallback callback = new ConfigurationCallback(configData);
                        merge.MergeEx(connection.PrimaryFeature, mergeRow.Directory, callback);
                    }
                    else
                    {
                        merge.Merge(connection.PrimaryFeature, mergeRow.Directory);
                    }

                    /*
                    IMsmErrors errorCollection = null;
                    merge.get_Errors(out errorCollection);
                    long count = errorCollection.get_Count();

                    if (0 < count)
                    {

                        throw new WixMergeFailureException(null, this.tempFiles.BasePath, count, null);
                    }
                    */

                    foreach (string connectTo in connection.ConnectFeatures)
                    {
                        merge.Connect(connectTo);
                    }

                    // if the module has files and creating layout
                    if (mergeRow.HasFiles && !this.suppressLayout)
                    {
                        string hashedMergeId = mergeRow.Id.GetHashCode().ToString("X4", CultureInfo.InvariantCulture.NumberFormat);

                        // extract the module cabinet, then explode all of the files to a temp directory
                        string moduleCabPath = String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, hashedMergeId, ".module.cab");
                        merge.ExtractCAB(moduleCabPath);

                        string mergeIdPath = String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, "MergeId.", hashedMergeId);
                        Directory.CreateDirectory(mergeIdPath);

                        WixExtractCab extCab = null;
                        try
                        {
                            extCab = new WixExtractCab();
                            extCab.Extract(moduleCabPath, mergeIdPath);
                        }
                        catch (WixCabExtractionException wce)
                        {
                            COMException comException = wce.InnerException as COMException;
                            foundError = true;
                            if (null != comException && 0x80070002 == unchecked((uint)comException.ErrorCode))
                            {
                                extCab = null; // Cab doesn't exist, so drop the object.
                                this.OnMessage(WixErrors.CabFileDoesNotExist(moduleCabPath, mergeModulePath, mergeIdPath));
                            }
                            else
                            {
                                this.OnMessage(WixErrors.CabExtractionFailed(moduleCabPath, mergeModulePath, mergeIdPath));
                            }
                        }
                        finally
                        {
                            if (null != extCab)
                            {
                                try
                                {
                                    extCab.Close();
                                }
                                catch (WixCabExtractionException)
                                {
                                    this.OnMessage(WixErrors.CabClosureFailed(moduleCabPath));
                                }
                            }
                        }
                    }

                    moduleOpen = false;
                    merge.CloseModule();
                }

                commit = !foundError; // if all seems to have progressed cleanly, feel free to commit the changes to the database
            }
            finally
            {
                if (moduleOpen)
                {
                    merge.CloseModule();
                }
                if (databaseOpen)
                {
                    merge.CloseDatabase(commit);
                }
                if (logOpen)
                {
                    merge.CloseLog();
                }
            }

            // create a Hashtable of all the suppressed sequence types
            Hashtable suppressedTableNames = new Hashtable();
            if (output.SuppressAdminSequence)
            {
                suppressedTableNames[Action.SequenceTypeToString(SequenceType.adminExecute)] = null;
                suppressedTableNames[Action.SequenceTypeToString(SequenceType.adminUI)] = null;
            }
            if (output.SuppressAdvertiseSequence)
            {
                suppressedTableNames[Action.SequenceTypeToString(SequenceType.advertiseExecute)] = null;
            }
            if (output.SuppressUISequence)
            {
                suppressedTableNames[Action.SequenceTypeToString(SequenceType.adminUI)] = null;
                suppressedTableNames[Action.SequenceTypeToString(SequenceType.installUI)] = null;
            }

            using (Database db = new Database(databasePath, OpenDatabase.Direct))
            {
                OutputTable suppressActionOutputTable = output.OutputTables["SuppressAction"];

                // suppress individual actions
                if (null != suppressActionOutputTable)
                {
                    foreach (OutputRow outputRow in suppressActionOutputTable.OutputRows)
                    {
                        if (db.TableExists((string)outputRow.Row[0]))
                        {
                            Row row = outputRow.Row;
                            string query = String.Format("SELECT * FROM {0} WHERE `Action` = '{1}'", row[0].ToString(), (string)row[1]);

                            using (View view = db.OpenExecuteView(query))
                            {
                                Record record;

                                if (view.Fetch(out record))
                                {
                                    this.OnMessage(WixWarnings.SuppressMergedAction((string)row[1], row[0].ToString()));
                                    view.Modify(ModifyView.Delete, record);
                                    record.Close();
                                }
                            }
                        }
                    }
                }

                // query for merge module actions in suppressed sequences and drop them
                foreach (string tableName in suppressedTableNames.Keys)
                {
                    if (!db.TableExists(tableName))
                    {
                        continue;
                    }

                    using (View view = db.OpenExecuteView(String.Concat("SELECT `Action` FROM ", tableName)))
                    {
                        Record resultRecord;
                        while (view.Fetch(out resultRecord))
                        {
                            this.OnMessage(WixWarnings.SuppressMergedAction(resultRecord.GetString(1), tableName));
                            resultRecord.Close();
                        }
                    }

                    // drop suppressed sequences
                    using (View view = db.OpenExecuteView(String.Concat("DROP TABLE ", tableName)))
                    {
                    }

                    // delete the validation rows
                    using (View view = db.OpenView(String.Concat("DELETE FROM _Validation WHERE `Table` = ?")))
                    {
                        Record record = new Record(1);
                        record.SetString(1, tableName);
                        view.Execute(record);
                    }
                }

                // now update the Attributes column for the files from the Merge Modules
                using (View view = db.OpenView("SELECT `Sequence`, `Attributes` FROM `File` WHERE `File`=?"))
                {
                    foreach (FileMediaInformation fmi in output.FileMediaInformationCollection)
                    {
                        if (!fmi.IsInModule)
                        {
                            continue;
                        }

                        Record record = new Record(1);
                        record.SetString(1, fmi.File);
                        view.Execute(record);

                        Record recordUpdate;
                        view.Fetch(out recordUpdate);

                        if (null == recordUpdate)
                        {
                            throw new WixMergeFailureException(null, this.tempFiles.BasePath, 1, null);
                        }

                        recordUpdate.SetInteger(1, fmi.Sequence);

                        // update the file attributes to match the compression specified
                        // on the Merge element or on the Package element
                        int attributes = 0;

                        // get the current value if its not null
                        if (!recordUpdate.IsNull(2))
                        {
                            attributes = recordUpdate.GetInteger(2);
                        }

                        if (FileCompressionValue.Yes == fmi.FileCompression)
                        {
                            attributes |= MsiInterop.MsidbFileAttributesCompressed;
                        }
                        else if (FileCompressionValue.No == fmi.FileCompression)
                        {
                            attributes |= MsiInterop.MsidbFileAttributesNoncompressed;
                        }
                        else // not specified
                        {
                            Debug.Assert(FileCompressionValue.NotSpecified == fmi.FileCompression);

                            // clear any compression bits
                            attributes &= ~MsiInterop.MsidbFileAttributesCompressed;
                            attributes &= ~MsiInterop.MsidbFileAttributesNoncompressed;
                        }
                        recordUpdate.SetInteger(2, attributes);

                        view.Modify(ModifyView.Update, recordUpdate);
                    }
                }

                db.Commit();
            }
        }