Пример #1
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);
            }
        }
Пример #2
0
        /// <summary>
        /// Adds all the streams to the final output.
        /// </summary>
        /// <param name="databasePath">Path to database.</param>
        /// <param name="output">Output object that points at final output.</param>
        private void ImportStreams(string databasePath, Output output)
        {
            using (Database db = new Database(databasePath, OpenDatabase.Direct))
            {
                View streamsView = null;
                View binaryView = null;
                View iconView = null;
                View certificateView = null;

                try
                {
                    streamsView = db.OpenExecuteView("SELECT `Name`, `Data` FROM `_Streams`");
                    if (db.TableExists("Binary"))
                    {
                        binaryView = db.OpenExecuteView("SELECT `Name`, `Data` FROM `Binary`");
                    }
                    if (db.TableExists("Icon"))
                    {
                        iconView = db.OpenExecuteView("SELECT `Name`, `Data` FROM `Icon`");
                    }
                    if (db.TableExists("MsiDigitalCertificate"))
                    {
                        certificateView = db.OpenExecuteView("SELECT `DigitalCertificate`, `CertData` FROM `MsiDigitalCertificate`");
                    }

                    foreach (ImportStream importStream in output.ImportStreams)
                    {
                        string src;
                        using (Record record = new Record(2))
                        {
                            try
                            {
                                switch (importStream.Type)
                                {
                                    case ImportStreamType.Cabinet:
                                        this.OnMessage(WixVerboses.ImportCabinetStream(importStream.StreamName, importStream.Path));

                                        record[1] = importStream.StreamName;
                                        record.SetStream(2, importStream.Path);
                                        streamsView.Modify(ModifyView.Assign, record);
                                        break;

                                    case ImportStreamType.DigitalCertificate:
                                        src = this.extension.FileResolutionHandler(importStream.Path, FileResolutionType.DigitalCertificate);
                                        this.OnMessage(WixVerboses.ImportDigitalCertificateStream(null, VerboseLevel.Trace, src));

                                        record[1] = importStream.StreamName;
                                        record.SetStream(2, importStream.Path);
                                        certificateView.Modify(ModifyView.Assign, record);
                                        break;

                                    case ImportStreamType.Binary:
                                        src = this.extension.FileResolutionHandler(importStream.Path, FileResolutionType.Binary);
                                        this.OnMessage(WixVerboses.ImportBinaryStream(null, VerboseLevel.Trace, src));

                                        if (OutputType.Module == output.Type)
                                        {
                                            record[1] = String.Concat(importStream.StreamName, ".", output.ModularizationGuid);
                                        }
                                        else
                                        {
                                            record[1] = importStream.StreamName;
                                        }
                                        if (55 < record[1].Length)
                                        {
                                            throw new WixInvalidAttributeException(null, "Binary", "Id", String.Format("Identifier cannot be longer than 55 characters.  Binary identifier: {0}", record[1]));
                                        }
                                        record.SetStream(2, src);
                                        binaryView.Modify(ModifyView.Assign, record);
                                        break;

                                    case ImportStreamType.Icon:
                                        src = this.extension.FileResolutionHandler(importStream.Path, FileResolutionType.Icon);
                                        this.OnMessage(WixVerboses.ImportIconStream(null, VerboseLevel.Verbose, src));

                                        if (OutputType.Module == output.Type)
                                        {
                                            int start = importStream.StreamName.LastIndexOf(".");
                                            if (-1 == start)
                                            {
                                                record[1] = String.Concat(importStream.StreamName, ".", output.ModularizationGuid);
                                            }
                                            else
                                            {
                                                record[1] = String.Concat(importStream.StreamName.Substring(0, start), ".", output.ModularizationGuid, importStream.StreamName.Substring(start));
                                            }
                                        }
                                        else
                                        {
                                            record[1] = importStream.StreamName;
                                        }
                                        if (55 < record[1].Length)
                                        {
                                            throw new WixInvalidAttributeException(null, "Icon", "Id", String.Format("Identifier cannot be longer than 55 characters.  Icon identifier: {0}", record[1]));
                                        }
                                        record.SetStream(2, src);
                                        iconView.Modify(ModifyView.Assign, record);
                                        break;

                                    default:
                                        throw new ArgumentException(String.Format("unknown import stream type: {0}, name: {1}", importStream.Type, importStream.StreamName), "importStream");
                                }
                            }
                            catch (WixFileNotFoundException wfnfe)
                            {
                                this.OnMessage(WixErrors.BinderExtensionMissingFile(null, ErrorLevel.Normal, wfnfe.Message));
                            }
                        }
                    }

                    db.Commit();
                }
                catch (FileNotFoundException fnfe)
                {
                    throw new WixFileNotFoundException(null, fnfe.FileName, fnfe);
                }
                finally
                {
                    if (null != certificateView)
                    {
                        certificateView.Close();
                    }
                    if (null != iconView)
                    {
                        iconView.Close();
                    }
                    if (null != binaryView)
                    {
                        binaryView.Close();
                    }
                    if (null != streamsView)
                    {
                        streamsView.Close();
                    }
                }
            }
        }