Пример #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>
        /// Instantiate a new MsiException with a given error.
        /// </summary>
        /// <param name="error">The error code from the MsiXxx() function call.</param>
        public MsiException(int error)
            : base(error)
        {
            uint handle = MsiInterop.MsiGetLastErrorRecord();
            if (0 != handle)
            {
                using (Record record = new Record(handle))
                {
                    this.MsiError = record.GetInteger(1);

                    int errorInfoCount = record.GetFieldCount() - 1;
                    this.ErrorInfo = new string[errorInfoCount];
                    for (int i = 0; i < errorInfoCount; ++i)
                    {
                        this.ErrorInfo[i] = record.GetString(i + 2);
                    }
                }
            }
            else
            {
                this.MsiError = 0;
                this.ErrorInfo = new string[0];
            }

            this.Error = error;
        }
Пример #3
0
        /// <summary>
        /// Query an MSI table for a single value
        /// </summary>
        /// <param name="msi">The path to an MSI</param>
        /// <param name="sql">An MSI query</param>
        /// <returns>The results as a string or null if no results are returned</returns>
        /// <remarks>
        /// Returns the value of the first field in the first record
        /// </remarks>
        public static string Query(string msi, string query)
        {
            string result = null;

            using (Database database = new Database(msi, OpenDatabase.ReadOnly))
            {
                using (View view = database.OpenExecuteView(query))
                {
                    using (Microsoft.Tools.WindowsInstallerXml.Msi.Record record = view.Fetch())
                    {
                        if (null != record)
                        {
                            result = Convert.ToString(record.GetString(1));
                        }
                    }
                }
            }

            return(result);
        }
Пример #4
0
        /// <summary>
        /// Process uncompressed files.
        /// </summary>
        /// <param name="tempDatabaseFile">The temporary database file.</param>
        /// <param name="fileRows">The collection of files to copy into the image.</param>
        /// <param name="fileTransfers">Array of files to be transfered.</param>
        /// <param name="mediaRows">The indexed media rows.</param>
        /// <param name="layoutDirectory">The directory in which the image should be layed out.</param>
        /// <param name="compressed">Flag if source image should be compressed.</param>
        /// <param name="longNamesInImage">Flag if long names should be used.</param>
        private void ProcessUncompressedFiles(string tempDatabaseFile, FileRowCollection fileRows, ArrayList fileTransfers, MediaRowCollection mediaRows, string layoutDirectory, bool compressed, bool longNamesInImage)
        {
            if (0 == fileRows.Count || this.core.EncounteredError)
            {
                return;
            }

            Hashtable directories = new Hashtable();
            using (Database db = new Database(tempDatabaseFile, OpenDatabase.ReadOnly))
            {
                using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
                {
                    while (true)
                    {
                        using (Record directoryRecord = directoryView.Fetch())
                        {
                            if (null == directoryRecord)
                            {
                                break;
                            }

                            string sourceName = Installer.GetName(directoryRecord.GetString(3), true, longNamesInImage);

                            directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName));
                        }
                    }
                }

                using (View fileView = db.OpenView("SELECT `Directory_`, `FileName` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_` AND `File`.`File`=?"))
                {
                    using (Record fileQueryRecord = new Record(1))
                    {
                        // for each file in the array of uncompressed files
                        foreach (FileRow fileRow in fileRows)
                        {
                            string relativeFileLayoutPath = null;

                            string mediaLayoutDirectory = this.FileManager.ResolveMedia(mediaRows[fileRow.DiskId], layoutDirectory);

                            // setup up the query record and find the appropriate file in the
                            // previously executed file view
                            fileQueryRecord[1] = fileRow.File;
                            fileView.Execute(fileQueryRecord);

                            using (Record fileRecord = fileView.Fetch())
                            {
                                if (null == fileRecord)
                                {
                                    throw new WixException(WixErrors.FileIdentifierNotFound(fileRow.SourceLineNumbers, fileRow.File));
                                }

                                relativeFileLayoutPath = Binder.GetFileSourcePath(directories, fileRecord[1], fileRecord[2], compressed, longNamesInImage);
                            }

                            // finally put together the base media layout path and the relative file layout path
                            string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath);
                            FileTransfer transfer;
                            if (FileTransfer.TryCreate(fileRow.Source, fileLayoutPath, false, "File", fileRow.SourceLineNumbers, out transfer))
                            {
                                fileTransfers.Add(transfer);
                            }
                        }
                    }
                }
            }
        }
Пример #5
0
        /// <summary>
        /// Processes the feature record under the specified parent.
        /// </summary>
        /// <param name="writer">XmlWriter where the Intermediate should persist itself as XML.</param>
        /// <param name="parentWriter">Parent XmlWriter to point to add the reference.</param>
        /// <param name="parent">Parent feature for this feature.</param>
        /// <param name="record">Feature record.</param>
        private void ProcessFeatureRecord(XmlWriter writer, XmlWriter parentWriter, string parent, Record record)
        {
            const string tableName = "Feature";
            const string tableNameRef = "FeatureRef";

            string featureName = record[(int)MsiInterop.Feature.Feature];
            this.core.OnMessage(WixVerboses.ProcessingFeature(null, VerboseLevel.Verbose, featureName));

            int displayLevel = 0;
            string display = null;
            int bits = Convert.ToInt32(record[(int)MsiInterop.Feature.Attributes]); // non-nullable field
            if (0 < record[(int)MsiInterop.Feature.Display].Length)
            {
                displayLevel = Convert.ToInt32(record[(int)MsiInterop.Feature.Display]);
            }

            if (0 == displayLevel)
            {
                display = "hidden";
            }
            else if (0 < (displayLevel & 1)) // is the first bit set
            {
                display = "expand";
            }

            if (this.generateFragments)
            {
                writer.WriteStartElement("Fragment");
            }

            writer.WriteStartElement(tableName);
            this.core.WriteAttributeString(writer, "Id", featureName);
            this.core.WriteAttributeString(writer, "Title", record[(int)MsiInterop.Feature.Title]);
            this.core.WriteAttributeString(writer, "Description", record[(int)MsiInterop.Feature.Description]);
            this.core.WriteAttributeString(writer, "Display", display);

            // if the condition is null then the level in the condition is actual level for the feature
            string level = record[(int)MsiInterop.Feature.Level];
            if (this.inputDatabase.TableExists("Condition"))
            {
                using (View conditionView = this.inputDatabase.OpenExecuteView(String.Concat("SELECT * FROM `Condition` WHERE `Feature_` = '", featureName, "'")))
                {
                    Record conditionRecord;
                    while (conditionView.Fetch(out conditionRecord))
                    {
                        string condition = conditionRecord[(int)MsiInterop.Condition.Condition];
                        if (0 == condition.Length)
                        {
                            this.core.OnMessage(WixWarnings.NullConditionInConditionTable(null, WarningLevel.Major, featureName));
                            level = conditionRecord[(int)MsiInterop.Condition.Level];
                        }
                    }
                }
            }

            this.core.WriteAttributeString(writer, "Level", level);
            this.core.WriteAttributeString(writer, "ConfigurableDirectory", record[(int)MsiInterop.Feature.Directory]);
            if (0 < (bits & MsiInterop.MsidbFeatureAttributesFavorSource))
            {
                this.core.WriteAttributeString(writer, "InstallDefault", "source");
            }

            if (0 < (bits & MsiInterop.MsidbFeatureAttributesFavorAdvertise))
            {
                this.core.WriteAttributeString(writer, "TypicalDefault", "advertise");
            }

            if (0 < (bits & MsiInterop.MsidbFeatureAttributesFollowParent))
            {
                this.core.WriteAttributeString(writer, "InstallDefault", "followParent");
            }

            if (0 < (bits & MsiInterop.MsidbFeatureAttributesUIDisallowAbsent))
            {
                this.core.WriteAttributeString(writer, "Absent", "disallow");
            }

            if (0 < (bits & MsiInterop.MsidbFeatureAttributesDisallowAdvertise))
            {
                this.core.WriteAttributeString(writer, "AllowAdvertise", "no");
            }
            else if (0 < (bits & MsiInterop.MsidbFeatureAttributesNoUnsupportedAdvertise))
            {
                this.core.WriteAttributeString(writer, "AllowAdvertise", "system");
            }
            this.ProcessConditionTable(writer, featureName);
            this.ProcessFeatureComponentTable(writer, featureName);

            if (this.generateFragments)
            {
                parentWriter.WriteStartElement(tableNameRef);
                this.core.WriteAttributeString(parentWriter, "Id", featureName);
                parentWriter.WriteEndElement();
            }
            this.ProcessFeatureTable(writer, featureName);

            // support decompiler extension here
            foreach (DecompilerExtension extension in this.extensionList)
            {
                extension.ExtendChildrenOfElement("Feature", featureName);
            }

            if (this.generateFragments)
            {
                writer.WriteEndElement(); // close parent (sometimes self)
            }
            writer.WriteEndElement(); // close fragment or self
        }
Пример #6
0
        /// <summary>
        /// Lays out the binaries for the uncompressed portion of a source image.
        /// </summary>
        /// <param name="databasePath">Path to database.</param>
        /// <param name="output">Output being created.</param>
        /// <param name="files">Array of files to copy into image.</param>
        /// <param name="packageCompressed">Flag if package is compressed.</param>
        /// <param name="fileTransfers">Array of files to be transfered.</param>
        private void CreateUncompressedImage(string databasePath, Output output, ArrayList files, bool packageCompressed, ArrayList fileTransfers)
        {
            if (0 == files.Count || this.foundError)
            {
                return;
            }

            bool longNamesInImage = output.LongFileNames;
            Hashtable directories = new Hashtable();
            using (Database db = new Database(databasePath, OpenDatabase.ReadOnly))
            {
                using (View directoryView = db.OpenExecuteView("SELECT `Directory`, `Directory_Parent`, `DefaultDir` FROM `Directory`"))
                {
                    Record directoryRecord;

                    while (directoryView.Fetch(out directoryRecord))
                    {
                        string sourceName = GetSourceName(directoryRecord.GetString(3), longNamesInImage);

                        directories.Add(directoryRecord.GetString(1), new ResolvedDirectory(directoryRecord.GetString(2), sourceName));
                    }
                }

                using (View fileView = db.OpenView("SELECT `Directory_`, `FileName` FROM `Component`, `File` WHERE `Component`.`Component`=`File`.`Component_` AND `File`.`File`=?"))
                {
                    // if an output path was specified for our image, use that as our default base,
                    // otherwise use the directory where the output is being placed
                    string defaultBaseOuputPath = null != this.imagebaseOutputPath ? this.imagebaseOutputPath : Path.GetDirectoryName(output.Path);

                    using (Record fileQueryRecord = new Record(1))
                    {
                        // for each file in the array of uncompressed files
                        foreach (FileMediaInformation fmi in files)
                        {
                            string currentSourcePath = null;
                            string relativeSourcePath = null;

                            // determine what the base of the file should be.  If there was
                            // no src specified in the Media element (the default) then just
                            // use the default output path (usually the same directory as the
                            // output file).  If there was a build directory specified then
                            // check if it is a absolute path, and if not add the default
                            // output path to the root
                            MediaRow mediaRow = output.MediaRows[fmi.Media];
                            string baseRelativeSourcePath = mediaRow.Layout;
                            if (null == baseRelativeSourcePath)
                            {
                                baseRelativeSourcePath = defaultBaseOuputPath;
                            }
                            else if (!Path.IsPathRooted(baseRelativeSourcePath))
                            {
                                baseRelativeSourcePath = Path.Combine(defaultBaseOuputPath, baseRelativeSourcePath);
                            }

                            // setup up the query record and find the appropriate file in the
                            // previously executed file view
                            fileQueryRecord[1] = fmi.File;
                            fileView.Execute(fileQueryRecord);

                            Record fileRecord;
                            if (!fileView.Fetch(out fileRecord))
                            {
                                throw new WixFileMediaInformationKeyNotFoundException(fmi.File);
                            }

                            string fileName = GetSourceName(fileRecord[2], longNamesInImage);

                            if (packageCompressed)
                            {
                                // use just the file name of the file since all uncompressed files must appear
                                // in the root of the image in a compressed package
                                relativeSourcePath = fileName;
                            }
                            else
                            {
                                // get the relative path of where we want the source to be as specified
                                // in the Directory table
                                string directoryPath = GetDirectoryPath(directories, fileRecord[1], longNamesInImage);
                                relativeSourcePath = Path.Combine(directoryPath, fileName);
                            }

                            // if the relative source path was not resolved above then we have to bail
                            if (null == relativeSourcePath)
                            {
                                throw new WixFileMediaInformationKeyNotFoundException(fmi.File);
                            }

                            // strip off "SourceDir" if it's still on there
                            if (relativeSourcePath.StartsWith("SourceDir\\"))
                            {
                                relativeSourcePath = relativeSourcePath.Substring(10);
                            }

                            // resolve the src path for the file and ensure it exists
                            try
                            {
                                currentSourcePath = this.extension.FileResolutionHandler(fmi.Source, FileResolutionType.File);
                            }
                            catch (WixFileNotFoundException wfnfe)
                            {
                                this.OnMessage(WixErrors.BinderExtensionMissingFile(null, ErrorLevel.Normal, wfnfe.Message));
                                continue;
                            }

                            if (!(File.Exists(currentSourcePath)))
                            {
                                this.OnMessage(WixErrors.CannotFindFile(null, ErrorLevel.Normal, fmi.FileId, fmi.File, fmi.Source));
                                continue;
                            }

                            // finally put together the base image output path and the resolved source path
                            string resolvedSourcePath = Path.Combine(baseRelativeSourcePath, relativeSourcePath);

                            // if the current source path (where we know that the file already exists) and the resolved
                            // path as dictated by the Directory table are not the same, then propagate the file.  The
                            // image that we create may have already been done by some other process other than the linker, so
                            // there is no reason to copy the files to the resolved source if they are already there.
                            if (0 != String.Compare(Path.GetFullPath(currentSourcePath), Path.GetFullPath(resolvedSourcePath), true))
                            {
                                // just put the file in the transfers array, how anti-climatic
                                fileTransfers.Add(new FileTransfer(currentSourcePath, resolvedSourcePath, false));
                            }
                        }
                    }
                }
            }
        }
Пример #7
0
        /// <summary>
        /// Returns a record containing column names or definitions.
        /// </summary>
        /// <param name="columnType">Specifies a flag indicating what type of information is needed. Either MSICOLINFO_NAMES or MSICOLINFO_TYPES.</param>
        /// <param name="record">Record to get column info about.</param>
        public void GetColumnInfo(int columnType, out Record record)
        {
            if (IntPtr.Zero == handle)
            {
                throw new ArgumentNullException();   // TODO: come up with a real exception to throw
            }

            IntPtr recordHandle;
            uint error = MsiInterop.MsiViewGetColumnInfo(this.handle, columnType, out recordHandle);
            if (0 != error)
            {
                throw new System.Runtime.InteropServices.ExternalException("Failed to get column info on view", (int)error);
            }
            record = new Record(recordHandle);
        }
Пример #8
0
        /// <summary>
        /// Executes a query substituing the values from the records into the customizable parameters
        /// in the view.
        /// </summary>
        /// <param name="record">Record containing parameters to be substituded into the view.</param>
        public void Execute(Record record)
        {
            if (IntPtr.Zero == this.handle)
            {
                throw new ArgumentNullException();   // TODO: come up with a real exception to throw
            }

            uint error = MsiInterop.MsiViewExecute(this.handle, null == record ? IntPtr.Zero : record.InternalHandle);
            if (0 != error)
            {
                throw new System.Runtime.InteropServices.ExternalException("Failed to execute view", (int)error);
            }
        }
Пример #9
0
        /// <summary>
        /// Processes Component table for a given directory.
        /// </summary>
        /// <param name="writer">XmlWriter which should will persist the XML.</param>
        /// <param name="directory">Directory node to process Components for.</param>
        /// <param name="rootPathShort">Directory path to process components for.</param>
        /// <param name="rootPathLong">Directory path to process components for.</param>
        /// <param name="record">Record from the component table to process.</param>
        private void ProcessComponentRecord(XmlWriter writer, string directory, string rootPathShort, string rootPathLong, Record record)
        {
            const string tableName = "Component";
            string id = record[(int)MsiInterop.Component.ComponentId];
            string rawComponent = record[(int)MsiInterop.Component.Component];
            string component = this.core.GetValidIdentifier(rawComponent, "Component");

            if ((0 < id.Length && !this.processedComponents.ContainsKey(id)) || !this.processedComponents.ContainsKey(rawComponent))
            {
                string condition = record[(int)MsiInterop.Component.Condition].Trim();

                int bits = Convert.ToInt32(record[(int)MsiInterop.Component.Attributes]); // non-nullable field
                string keyPath = record[(int)MsiInterop.Component.KeyPath];
                string regKeyPath = null;
                string fileKeyPath = null;
                string odbcKeyPath = null;

                if (this.generateFragments)
                {
                    writer.WriteStartElement("Fragment");
                    if (null != directory)
                    {
                        writer.WriteStartElement("DirectoryRef");
                        string directoryId = this.StripModuleId(directory);
                        this.core.WriteAttributeString(writer, "Id", directoryId);
                    }
                }
                writer.WriteStartElement(tableName);
                this.core.WriteAttributeString(writer, "Id", this.StripModuleId(component));
                this.core.WriteAttributeString(writer, null, "Guid", this.StripBraces(id), true);

                if (0 < keyPath.Length)
                {
                    if (0 < (bits & MsiInterop.MsidbComponentAttributesRegistryKeyPath))
                    {
                        regKeyPath  = keyPath;
                    }
                    else if (0 < (bits & MsiInterop.MsidbComponentAttributesODBCDataSource))
                    {
                        odbcKeyPath = keyPath;
                    }
                    else
                    {
                        fileKeyPath = keyPath;
                    }
                }
                else // the Component is using the directory as the keypath
                {
                    this.core.WriteAttributeString(writer, "KeyPath", "yes");
                }

                if (0 < (bits & MsiInterop.MsidbComponentAttributesSharedDllRefCount))
                {
                    this.core.WriteAttributeString(writer, "SharedDllRefCount", "yes");
                }

                if (0 < (bits & MsiInterop.MsidbComponentAttributesPermanent))
                {
                    this.core.WriteAttributeString(writer, "Permanent", "yes");
                }

                if (0 < (bits & MsiInterop.MsidbComponentAttributesTransitive))
                {
                    this.core.WriteAttributeString(writer, "Transitive", "yes");
                }

                if (0 < (bits & MsiInterop.MsidbComponentAttributesNeverOverwrite))
                {
                    this.core.WriteAttributeString(writer, "NeverOverwrite", "yes");
                }

                if (0 < (bits & MsiInterop.MsidbComponentAttributesOptional))
                {
                    this.core.WriteAttributeString(writer, "Location", "either");
                }
                else if (0 < (bits & MsiInterop.MsidbComponentAttributesSourceOnly))
                {
                    this.core.WriteAttributeString(writer, "Location", "source");
                }

                if (0 < (bits & MsiInterop.MsidbComponentAttributesDisableRegistryReflection))
                {
                    this.core.WriteAttributeString(writer, "DisableRegistryReflection", "yes");
                }

                if (this.inputDatabase.TableExists("Complus"))
                {
                    using (View complusView = this.inputDatabase.OpenExecuteView(String.Concat("SELECT * FROM `Complus` WHERE `Component_`='", rawComponent, "'")))
                    {
                        Record complusRecord;
                        while (complusView.Fetch(out complusRecord))
                        {
                            this.core.WriteAttributeString(writer, "ComPlusFlags", complusRecord[(int)MsiInterop.Complus.ExpType]);
                        }
                    }
                }

                // support decompiler extension here
                foreach (DecompilerExtension extension in this.extensionList)
                {
                    extension.ExtendAttributesOfElement("Component", rawComponent);
                }

                if (0 < condition.Length)
                {
                    writer.WriteStartElement("Condition");
                    writer.WriteCData(condition);
                    writer.WriteEndElement();
                }

                this.ProcessFileTable(writer, rawComponent, fileKeyPath, rootPathShort, rootPathLong);
                this.ProcessCreateFolderTable(writer, rawComponent, directory);
                this.ProcessRegistryTable(writer, rawComponent, regKeyPath, "Registry");
                this.ProcessRegistryTable(writer, rawComponent, regKeyPath, "RemoveRegistry");
                this.ProcessServiceControlTable(writer, rawComponent);
                this.ProcessServiceInstallTable(writer, rawComponent);
                this.ProcessEnvironmentTable(writer, rawComponent);
                this.ProcessRemoveFileTable(writer, rawComponent);
                this.ProcessReserveCostTable(writer, rawComponent);
                this.ProcessShortcutTable(writer, rawComponent);
                this.ProcessClassTable(writer, rawComponent);
                this.ProcessExtensionTable(writer, rawComponent, null);
                this.ProcessIniFileTable(writer, rawComponent, "IniFile");
                this.ProcessIniFileTable(writer, rawComponent, "RemoveIniFile");
                this.ProcessMoveFileTable(writer, rawComponent);
                this.ProcessDuplicateFileTable(writer, rawComponent, null);
                this.ProcessIsolatedComponentTable(writer, rawComponent);
                this.ProcessTypeLibTable(writer, rawComponent);
                this.ProcessPublishComponentTable(writer, rawComponent);
                this.ProcessODBCRootTable(writer, rawComponent, null, "ODBCDriver");
                this.ProcessODBCRootTable(writer, rawComponent, null, "ODBCTranslator");
                this.ProcessODBCDataSourceTable(writer, rawComponent, null, keyPath);

                // support decompiler extension here
                foreach (DecompilerExtension extension in this.extensionList)
                {
                    extension.ExtendChildrenOfElement("Component", rawComponent);
                }

                if (this.generateFragments)
                {
                    if (null != directory)
                    {
                        writer.WriteEndElement(); // close self
                    }
                    writer.WriteEndElement(); // close parent (sometimes self)
                    writer.WriteEndElement(); // close fragment
                }
                else
                {
                    writer.WriteEndElement();
                }

                if (0 < id.Length)
                {
                    this.processedComponents.Add(id, 1);
                }
                else
                {
                    this.processedComponents.Add(rawComponent, 1);
                }
            }
        }
Пример #10
0
        /// <summary>
        /// Print File Search
        /// </summary>
        /// <param name="writer">XmlWriter where the Intermediate should persist itself as XML.</param>
        /// <param name="fileRecord">file record.</param>
        private void PrintFileSearch(XmlWriter writer, Record fileRecord)
        {
            if (fileRecord != null)
            {
                string signature = fileRecord[(int)MsiInterop.Signature.Signature];
                this.core.OnMessage(WixVerboses.ProcessingFileSearchSignature(null, VerboseLevel.Verbose, signature));

                if (this.fileSearchSignatures.ContainsKey(this.StripModuleId(signature)))
                {
                    writer.WriteStartElement("FileSearchRef");
                    this.core.WriteAttributeString(writer, "Id", this.StripModuleId(signature));
                }
                else
                {
                    this.fileSearchSignatures[this.StripModuleId(signature)] = true;
                    string fileName = fileRecord[(int)MsiInterop.Signature.FileName];
                    string minversion = fileRecord[(int)MsiInterop.Signature.MinVersion];
                    string maxversion = fileRecord[(int)MsiInterop.Signature.MaxVersion];
                    string minsize = fileRecord[(int)MsiInterop.Signature.MinSize];
                    string maxsize = fileRecord[(int)MsiInterop.Signature.MaxSize];
                    string mindate = this.core.ConvertMSIDateToXmlDate(fileRecord[(int)MsiInterop.Signature.MinDate]);
                    string maxdate = this.core.ConvertMSIDateToXmlDate(fileRecord[(int)MsiInterop.Signature.MaxDate]);
                    string languages = fileRecord[(int)MsiInterop.Signature.Languages];

                    // due to a Windows Installer bug, it's legal to have either the Name or LongName
                    // attribute or both attributes
                    string name = null;
                    string longName = null;
                    string[] fileNames = fileName.Split("|".ToCharArray());
                    if (2 == fileNames.Length) // both short and long names
                    {
                        name = fileNames[0];
                        longName = fileNames[1];
                    }
                    else if (CompilerCore.IsValidShortFilename(fileName)) // only short name is present
                    {
                        name = fileName;
                    }
                    else // only long name is present
                    {
                        longName = fileName;
                    }

                    writer.WriteStartElement("FileSearch");
                    this.core.WriteAttributeString(writer, "Id", this.StripModuleId(signature));
                    this.core.WriteAttributeString(writer, "LongName", longName);
                    this.core.WriteAttributeString(writer, "Name", name);
                    this.core.WriteAttributeString(writer, "MinVersion", minversion);
                    this.core.WriteAttributeString(writer, "MaxVersion", maxversion);
                    this.core.WriteAttributeString(writer, "MinSize", minsize);
                    this.core.WriteAttributeString(writer, "MaxSize", maxsize);
                    this.core.WriteAttributeString(writer, "MinDate", mindate);
                    this.core.WriteAttributeString(writer, "MaxDate", maxdate);
                    this.core.WriteAttributeString(writer, "Languages", languages);
                }
                writer.WriteEndElement();
            }
        }
Пример #11
0
        /// <summary>
        /// Unbind an MSI database file.
        /// </summary>
        /// <param name="databaseFile">The database file.</param>
        /// <param name="database">The opened database.</param>
        /// <param name="outputType">The type of output to create.</param>
        /// <param name="exportBasePath">The path where files should be exported.</param>
        /// <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;
        }
Пример #12
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));
                        }
                    }
                }
            }
        }
Пример #13
0
 /// <summary>
 /// Updates a fetched record.
 /// </summary>
 /// <param name="type">Type of modification mode.</param>
 /// <param name="record">Record to be modified.</param>
 public void Modify(ModifyView type, Record record)
 {
     int error = MsiInterop.MsiViewModify(this.Handle, Convert.ToInt32(type, CultureInfo.InvariantCulture), record.Handle);
     if (0 != error)
     {
         throw new MsiException(error);
     }
 }
Пример #14
0
 /// <summary>
 /// Executes a query substituing the values from the records into the customizable parameters 
 /// in the view.
 /// </summary>
 /// <param name="record">Record containing parameters to be substituded into the view.</param>
 public void Execute(Record record)
 {
     int error = MsiInterop.MsiViewExecute(this.Handle, null == record ? 0 : record.Handle);
     if (0 != error)
     {
         throw new MsiException(error);
     }
 }
Пример #15
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();
                    }
                }
            }
        }
Пример #16
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);
            }
        }
Пример #17
0
        /// <summary>
        /// Executes the view to fetch the value for a specified property.
        /// </summary>
        /// <param name="view">View that is already open on the Property table.</param>
        /// <param name="propertyName">Name of the property to get the value for.</param>
        /// <returns>String value of the property.</returns>
        private string FetchPropertyValue(View view, string propertyName)
        {
            string propertyValue = null;
            using (Record recIn = new Record(1))
            {
                recIn[1] = propertyName;
                view.Execute(recIn);

                using (Record recOut = view.Fetch())
                {
                    if (recOut != null)
                    {
                        propertyValue = recOut[1];
                    }
                }
            }

            return propertyValue;
        }
Пример #18
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])));
            }
        }
Пример #19
0
        /// <summary>
        /// Processes the feature record under the specified parent.
        /// </summary>
        /// <param name="parentWriter">XmlWriter where the Intermediate should persist itself as XML.</param>
        /// <param name="record">record from the custom action table that needs to be estruded as XML.</param>
        private void ProcessCustomActionRecord(XmlWriter parentWriter, Record record)
        {
            const string tableName = "CustomAction";

            XmlWriter writer = parentWriter;
            string id = this.StripModuleId(this.core.GetValidIdentifier(record[(int)MsiInterop.CustomAction.Action], "CustomAction; Column: Action"));
            if (null == writer)
            {
                writer = this.InitializeXmlTextWriter(Path.GetFullPath(Path.Combine(this.outputFolder, String.Concat("BaseLibrary\\CustomActions\\", id, ".wxs"))));
            }

            if (this.generateFragments && null == parentWriter)
            {
                writer.WriteStartElement("Fragment");
                this.core.WriteAttributeString(writer, "Id", id);
            }

            string source = record[(int)MsiInterop.CustomAction.Source];
            string target = record[(int)MsiInterop.CustomAction.Target];

            // type is a non-nullable field
            int bits = Convert.ToInt32(record[(int)MsiInterop.CustomAction.Type]);

            writer.WriteStartElement(tableName);
            this.core.WriteAttributeString(writer, "Id", id);

            string returnType = null;
            switch (bits & MsiInterop.MsidbCustomActionTypeReturnBits)
            {
                case 0:
                    returnType = "check";
                    break;
                case MsiInterop.MsidbCustomActionTypeContinue:
                    returnType = "ignore";
                    break;
                case MsiInterop.MsidbCustomActionTypeAsync:
                    returnType = "asyncWait";
                    break;
                case MsiInterop.MsidbCustomActionTypeAsync + MsiInterop.MsidbCustomActionTypeContinue:
                    returnType = "asyncNoWait";
                    break;
                default:
                    // TODO: throw an exception
                    break;
            }
            this.core.WriteAttributeString(writer, "Return", returnType);

            string execute = null;
            switch (bits & MsiInterop.MsidbCustomActionTypeExecuteBits)
            {
                case 0:
                    execute = null;
                    break;
                case MsiInterop.MsidbCustomActionTypeFirstSequence:
                    execute = "firstSequence";
                    break;
                case MsiInterop.MsidbCustomActionTypeOncePerProcess:
                    execute = "oncePerProcess";
                    break;
                case MsiInterop.MsidbCustomActionTypeClientRepeat:
                    execute = "secondSequence";
                    break;
                case MsiInterop.MsidbCustomActionTypeInScript:
                    execute = "deferred";
                    break;
                case MsiInterop.MsidbCustomActionTypeInScript + MsiInterop.MsidbCustomActionTypeRollback:
                    execute = "rollback";
                    break;
                case MsiInterop.MsidbCustomActionTypeInScript + MsiInterop.MsidbCustomActionTypeCommit:
                    execute = "commit";
                    break;
                case 7:
                default:
                    // TODO: throw an exception
                    break;
            }
            this.core.WriteAttributeString(writer, "Execute", execute);

            if (0 < (bits & MsiInterop.MsidbCustomActionTypeNoImpersonate))
            {
                this.core.WriteAttributeString(writer, "Impersonate", "no");
            }

            string name = null;
            string customActionType = null;
            switch (bits & MsiInterop.MsidbCustomActionTypeSourceBits)
            {
                case MsiInterop.MsidbCustomActionTypeBinaryData:
                    name = "BinaryKey";
                    break;
                case MsiInterop.MsidbCustomActionTypeSourceFile:
                    name = "FileKey";
                    break;
                case MsiInterop.MsidbCustomActionTypeDirectory:
                    name = "Directory";
                    break;
                case MsiInterop.MsidbCustomActionTypeProperty:
                    name = "Property";
                    break;
                default:
                    // TODO: throw an exception
                    break;
            }

            bool canBeEmptyCustomActionTypeValue = false;
            switch (bits & MsiInterop.MsidbCustomActionTypeTypeBits)
            {
                case MsiInterop.MsidbCustomActionTypeDll:
                    customActionType = "DllEntry";
                    break;
                case MsiInterop.MsidbCustomActionTypeExe:
                    canBeEmptyCustomActionTypeValue = true;
                    customActionType = "ExeCommand";
                    break;
                case MsiInterop.MsidbCustomActionTypeTextData:
                    canBeEmptyCustomActionTypeValue = true;
                    customActionType = "Value";
                    break;
                case MsiInterop.MsidbCustomActionTypeJScript:
                    canBeEmptyCustomActionTypeValue = true;
                    customActionType = "JScriptCall";
                    break;
                case MsiInterop.MsidbCustomActionTypeVBScript:
                    canBeEmptyCustomActionTypeValue = true;
                    customActionType = "VBScriptCall";
                    break;
                case MsiInterop.MsidbCustomActionTypeInstall:
                    customActionType = "InstallProperties";
                    if ("Directory" == name)
                    {
                        name = "PackageProductCode";
                    }

                    if ("FileKey" == name)
                    {
                        name = "PackagePath";
                    }

                    if ("BinaryKey" == name)
                    {
                        name = "PackageSubstorage";
                    }
                    break;
                case 0:
                case 4:
                default:
                    // TODO: throw an exception:  Fail "Unsupported custom action type: "
                    break;
            }

            if ("FileKey" == name && "Value" == customActionType)
            {
                this.core.WriteAttributeString(writer, "Error", target);
            }
            else
            {
                if (0 < source.Length)
                {
                    source = this.StripModuleId(source);
                    if ("Directory" == name)
                    {
                        source = this.core.GetValidIdentifier(source, "CustomAction; Column: Source"); // nullable
                    }
                    this.core.WriteAttributeString(writer, name, source);

                    // target is a formatted field so it is possible the value is a
                    // foriegn key with a module id in it.
                    target = this.StripModuleId(target);

                    this.core.WriteAttributeString(writer, null, customActionType, target, canBeEmptyCustomActionTypeValue);
                }
                else
                {
                    if ("JScriptCall" == customActionType)
                    {
                        this.core.WriteAttributeString(writer, "Script", "jscript");
                    }
                    else if ("VBScriptCall" == customActionType)
                    {
                        this.core.WriteAttributeString(writer, "Script", "vbscript");
                    }
                    else
                    {
                        // TODO: throw an exception
                    }
                    writer.WriteCData(target);
                }
            }
            writer.WriteEndElement();
            if (this.generateFragments)
            {
                this.EmitSequence("AdminUISequence", writer, id);

                // write InstallUISequence
                this.EmitSequence("InstallUISequence", writer, id);
                this.EmitSequence("AdvertiseExecuteSequence", writer, id);
                this.EmitSequence("AdminExecuteSequence", writer, id);
                this.EmitSequence("InstallExecuteSequence", writer, id);
                if (null == parentWriter)
                {
                    writer.WriteEndElement();
                }
            }
        }
Пример #20
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();
                }
            }
        }
Пример #21
0
        /// <summary>
        /// Fetches the next row in the view.
        /// </summary>
        /// <param name="record">Record for recieving the data in the next row of the view.</param>
        /// <returns>Returns true if there was another record to be fetched and false if there wasn't.</returns>
        public bool Fetch(out Record record)
        {
            if (IntPtr.Zero == this.handle)
            {
                throw new ArgumentNullException();   // TODO: come up with a real exception to throw
            }

            IntPtr recordHandle;
            uint error = MsiInterop.MsiViewFetch(this.handle, out recordHandle);
            if (259 == error)
            {
                record = null;
                return false;
            }
            else if (0 != error)
            {
                throw new System.Runtime.InteropServices.ExternalException("Failed to fetch record from view", (int)error);
            }

            record = new Record(recordHandle);
            return true;
        }
Пример #22
0
        /// <summary>
        /// Process DrLocator table
        /// </summary>
        /// <param name="writer">XmlWriter where the Intermediate should persist itself as XML.</param>
        /// <param name="signature">Enclosing signature context.</param>
        /// <param name="fileRecord">Enclosing file context.</param>
        /// <param name="rootLocator">true if this is the root locator.</param>
        /// <param name="findRMCCP">true if one is processing RMCCP.</param>
        /// <returns>count of nested directories</returns>
        private int ProcessDrLocator(XmlWriter writer, string signature, Record fileRecord, bool rootLocator, bool findRMCCP)
        {
            const string tableName = "DrLocator";
            string parent = "";
            string path = "";
            string depth = "";
            int ndirs = 0;
            int ret = 0;
            int i = 0;

            if (!this.inputDatabase.TableExists(tableName))
            {
                return ret;
            }

            string query = String.Concat("SELECT * FROM `", tableName, "` WHERE `Signature_`='", signature, "'");
            if (findRMCCP)
            {
                query = String.Concat(query, " AND `Parent`='CCP_DRIVE'");
            }
            else
            {
                query = String.Concat(query, " AND `Parent`<>'CCP_DRIVE'");
            }

            using (View view = this.inputDatabase.OpenExecuteView(query))
            {
                Record record;
                while (view.Fetch(out record))
                {
                    parent = record[(int)MsiInterop.DrLocator.Parent];
                    if (0 < parent.Length)
                    {
                        if (!findRMCCP)
                        {
                            ndirs = this.ProcessDrLocator(writer, parent, null, false, findRMCCP);
                        }
                    }

                    path = record[(int)MsiInterop.DrLocator.Path];
                    depth = record[(int)MsiInterop.DrLocator.Depth];
                    string key = String.Concat(this.StripModuleId(signature), ".", parent);

                    if (this.directorySearchSignatures.ContainsKey(key))
                    {
                        writer.WriteStartElement("DirectorySearchRef");
                        this.core.WriteAttributeString(writer, "Id", this.StripModuleId(signature));
                        if (0 < parent.Length && 0 == ndirs)
                        {
                            this.core.WriteAttributeString(writer, "Parent", parent);
                        }
                        this.core.WriteAttributeString(writer, "Path", path);
                    }
                    else
                    {
                        this.searchSignatures[this.StripModuleId(signature)] = true;
                        this.directorySearchSignatures[key] = true;
                        writer.WriteStartElement("DirectorySearch");
                        this.core.WriteAttributeString(writer, "Id", this.StripModuleId(signature));
                        this.core.WriteAttributeString(writer, "Path", path);
                        this.core.WriteAttributeString(writer, "Depth", depth);
                    }

                    if (rootLocator)
                    {
                        this.PrintFileSearch(writer, fileRecord);
                        for (i = 0; i <= ndirs; i++)
                        {
                            writer.WriteEndElement();
                        }
                    }
                    else
                    {
                        ret = ndirs + 1;
                    }
                }
            }
            return ret;
        }
Пример #23
0
        /// <summary>
        /// Updates a fetched record.
        /// </summary>
        /// <param name="type">Type of modification mode.</param>
        /// <param name="record">Record to be modified.</param>
        public void Modify(ModifyView type, Record record)
        {
            if (IntPtr.Zero == this.handle)
            {
                throw new ArgumentNullException();   // TODO: come up with a real exception to throw
            }

            uint error = MsiInterop.MsiViewModify(handle, Convert.ToInt32(type), record.InternalHandle);
            if (0 != error)
            {
                throw new System.Runtime.InteropServices.ExternalException("Failed to modify view", (int)error);
            }
        }
Пример #24
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();
            }
        }