Exemplo n.º 1
1
Arquivo: Binder.cs Projeto: zooba/wix3
        /// <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();
            }
        }
Exemplo n.º 2
0
        public static TableRow[] GetRowsFromTable(Database msidb, string tableName)
        {
            if (!msidb.TableExists(tableName))
            {
                Trace.WriteLine(string.Format("Table name does {0} not exist Found.", tableName));
                return new TableRow[0];
            }

            string query = string.Concat("SELECT * FROM `", tableName, "`");
            using (var view = new ViewWrapper(msidb.OpenExecuteView(query)))
            {
                var /*<TableRow>*/ rows = new ArrayList(view.Records.Count);

                ColumnInfo[] columns = view.Columns;
                foreach (object[] values in view.Records)
                {
                    HybridDictionary valueCollection = new HybridDictionary(values.Length);
                    for (int cIndex = 0; cIndex < columns.Length; cIndex++)
                    {
                        valueCollection[columns[cIndex].Name] = values[cIndex];
                    }
                    rows.Add(new TableRow(valueCollection));
                }
                return (TableRow[]) rows.ToArray(typeof(TableRow));
            }
        }
Exemplo n.º 3
0
Arquivo: Binder.cs Projeto: zooba/wix3
        /// <summary>
        /// Retrieve files and their information from merge modules.
        /// </summary>
        /// <param name="output">Internal representation of the msi database to operate upon.</param>
        /// <param name="fileRows">The indexed file rows.</param>
        private void ProcessMergeModules(Output output, FileRowCollection fileRows)
        {
            Table wixMergeTable = output.Tables["WixMerge"];
            if (null != wixMergeTable)
            {
                IMsmMerge2 merge = NativeMethods.GetMsmMerge();

                // Get the output's minimum installer version
                int outputInstallerVersion = int.MinValue;
                Table summaryInformationTable = output.Tables["_SummaryInformation"];
                if (null != summaryInformationTable)
                {
                    foreach (Row row in summaryInformationTable.Rows)
                    {
                        if (14 == (int)row[0])
                        {
                            outputInstallerVersion = Convert.ToInt32(row[1], CultureInfo.InvariantCulture);
                            break;
                        }
                    }
                }

                foreach (Row row in wixMergeTable.Rows)
                {
                    bool containsFiles = false;
                    WixMergeRow wixMergeRow = (WixMergeRow)row;

                    try
                    {
                        // read the module's File table to get its FileMediaInformation entries and gather any other information needed from the module.
                        using (Database db = new Database(wixMergeRow.SourceFile, OpenDatabase.ReadOnly))
                        {
                            if (db.TableExists("File") && db.TableExists("Component"))
                            {
                                Hashtable uniqueModuleFileIdentifiers = System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable();

                                using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`"))
                                {
                                    // add each file row from the merge module into the file row collection (check for errors along the way)
                                    while (true)
                                    {
                                        using (Record record = view.Fetch())
                                        {
                                            if (null == record)
                                            {
                                                break;
                                            }

                                            // NOTE: this is very tricky - the merge module file rows are not added to the
                                            // file table because they should not be created via idt import.  Instead, these
                                            // rows are created by merging in the actual modules
                                            FileRow fileRow = new FileRow(null, this.core.TableDefinitions["File"]);
                                            fileRow.File = record[1];
                                            fileRow.Compressed = wixMergeRow.FileCompression;
                                            fileRow.Directory = record[2];
                                            fileRow.DiskId = wixMergeRow.DiskId;
                                            fileRow.FromModule = true;
                                            fileRow.PatchGroup = -1;
                                            fileRow.Source = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat), Path.DirectorySeparatorChar, record[1]);

                                            FileRow collidingFileRow = fileRows[fileRow.File];
                                            FileRow collidingModuleFileRow = (FileRow)uniqueModuleFileIdentifiers[fileRow.File];

                                            if (null == collidingFileRow && null == collidingModuleFileRow)
                                            {
                                                fileRows.Add(fileRow);

                                                // keep track of file identifiers in this merge module
                                                uniqueModuleFileIdentifiers.Add(fileRow.File, fileRow);
                                            }
                                            else // collision(s) detected
                                            {
                                                // case-sensitive collision with another merge module or a user-authored file identifier
                                                if (null != collidingFileRow)
                                                {
                                                    this.core.OnMessage(WixErrors.DuplicateModuleFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, collidingFileRow.File));
                                                }

                                                // case-insensitive collision with another file identifier in the same merge module
                                                if (null != collidingModuleFileRow)
                                                {
                                                    this.core.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, fileRow.File, collidingModuleFileRow.File));
                                                }
                                            }

                                            containsFiles = true;
                                        }
                                    }
                                }
                            }

                            // Get the summary information to detect the Schema
                            using (SummaryInformation summaryInformation = new SummaryInformation(db))
                            {
                                string moduleInstallerVersionString = summaryInformation.GetProperty(14);

                                try
                                {
                                    int moduleInstallerVersion = Convert.ToInt32(moduleInstallerVersionString, CultureInfo.InvariantCulture);
                                    if (moduleInstallerVersion > outputInstallerVersion)
                                    {
                                        this.core.OnMessage(WixWarnings.InvalidHigherInstallerVersionInModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, moduleInstallerVersion, outputInstallerVersion));
                                    }
                                }
                                catch (FormatException)
                                {
                                    throw new WixException(WixErrors.MissingOrInvalidModuleInstallerVersion(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile, moduleInstallerVersionString));
                                }
                            }
                        }
                    }
                    catch (FileNotFoundException)
                    {
                        throw new WixException(WixErrors.FileNotFound(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile));
                    }
                    catch (Win32Exception)
                    {
                        throw new WixException(WixErrors.CannotOpenMergeModule(wixMergeRow.SourceLineNumbers, wixMergeRow.Id, wixMergeRow.SourceFile));
                    }

                    // if the module has files and creating layout
                    if (containsFiles && !this.suppressLayout)
                    {
                        bool moduleOpen = false;
                        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;
                        }

                        try
                        {
                            merge.OpenModule(wixMergeRow.SourceFile, mergeLanguage);
                            moduleOpen = true;

                            string safeMergeId = wixMergeRow.Number.ToString(CultureInfo.InvariantCulture.NumberFormat);

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

                            string mergeIdPath = String.Concat(this.TempFilesLocation, Path.DirectorySeparatorChar, "MergeId.", safeMergeId);
                            Directory.CreateDirectory(mergeIdPath);

                            using (WixExtractCab extractCab = new WixExtractCab())
                            {
                                try
                                {
                                    extractCab.Extract(moduleCabPath, mergeIdPath);
                                }
                                catch (FileNotFoundException)
                                {
                                    throw new WixException(WixErrors.CabFileDoesNotExist(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath));
                                }
                                catch
                                {
                                    throw new WixException(WixErrors.CabExtractionFailed(moduleCabPath, wixMergeRow.SourceFile, mergeIdPath));
                                }
                            }
                        }
                        catch (COMException ce)
                        {
                            throw new WixException(WixErrors.UnableToOpenModule(wixMergeRow.SourceLineNumbers, wixMergeRow.SourceFile, ce.Message));
                        }
                        finally
                        {
                            if (moduleOpen)
                            {
                                merge.CloseModule();
                            }
                        }
                    }
                }
            }
        }
Exemplo n.º 4
0
        /// <summary>
        /// Validate a database.
        /// </summary>
        /// <param name="databaseFile">The database to validate.</param>
        /// <returns>true if validation succeeded; false otherwise.</returns>
        public bool Validate(string databaseFile)
        {
            Dictionary<string, string> indexedICEs = new Dictionary<string, string>();
            Dictionary<string, string> indexedSuppressedICEs = new Dictionary<string, string>();
            int previousUILevel = (int)InstallUILevels.Basic;
            IntPtr previousHwnd = IntPtr.Zero;
            InstallUIHandler previousUIHandler = null;

            if (null == databaseFile)
            {
                throw new ArgumentNullException("databaseFile");
            }

            // initialize the validator extension
            this.extension.DatabaseFile = databaseFile;
            this.extension.Output = this.output;
            this.extension.InitializeValidator();

            // if we don't have the temporary files object yet, get one
            if (null == this.tempFiles)
            {
                this.tempFiles = new TempFileCollection();
            }
            Directory.CreateDirectory(this.TempFilesLocation); // ensure the base path is there

            // index the ICEs
            if (null != this.ices)
            {
                foreach (string ice in this.ices)
                {
                    indexedICEs[ice] = null;
                }
            }

            // index the suppressed ICEs
            if (null != this.suppressedICEs)
            {
                foreach (string suppressedICE in this.suppressedICEs)
                {
                    indexedSuppressedICEs[suppressedICE] = null;
                }
            }

            // copy the database to a temporary location so it can be manipulated
            string tempDatabaseFile = Path.Combine(this.TempFilesLocation, Path.GetFileName(databaseFile));
            File.Copy(databaseFile, tempDatabaseFile);

            // remove the read-only property from the temporary database
            FileAttributes attributes = File.GetAttributes(tempDatabaseFile);
            File.SetAttributes(tempDatabaseFile, attributes & ~FileAttributes.ReadOnly);

            Mutex mutex = new Mutex(false, "WixValidator");
            try
            {
                if (!mutex.WaitOne(0))
                {
                    this.OnMessage(WixVerboses.ValidationSerialized());
                    mutex.WaitOne();
                }

                using (Database database = new Database(tempDatabaseFile, OpenDatabase.Direct))
                {
                    bool propertyTableExists = database.TableExists("Property");
                    string productCode = null;

                    // remove the product code from the database before opening a session to prevent opening an installed product
                    if (propertyTableExists)
                    {
                        using (View view = database.OpenExecuteView("SELECT `Value` FROM `Property` WHERE Property = 'ProductCode'"))
                        {
                            using (Record record = view.Fetch())
                            {
                                if (null != record)
                                {
                                    productCode = record.GetString(1);

                                    using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"))
                                    {
                                    }
                                }
                            }
                        }
                    }

                    // merge in the cube databases
                    foreach (string cubeFile in this.cubeFiles)
                    {
                        try
                        {
                            using (Database cubeDatabase = new Database(cubeFile, OpenDatabase.ReadOnly))
                            {
                                try
                                {
                                    database.Merge(cubeDatabase, "MergeConflicts");
                                }
                                catch
                                {
                                    // ignore merge errors since they are expected in the _Validation table
                                }
                            }
                        }
                        catch (Win32Exception e)
                        {
                            if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
                            {
                                throw new WixException(WixErrors.CubeFileNotFound(cubeFile));
                            }

                            throw;
                        }
                    }

                    // commit the database before proceeding to ensure the streams don't get confused
                    database.Commit();

                    // the property table may have been added to the database
                    // from a cub database without the proper validation rows
                    if (!propertyTableExists)
                    {
                        using (View view = database.OpenExecuteView("DROP table `Property`"))
                        {
                        }
                    }

                    // get all the action names for ICEs which have not been suppressed
                    List<string> actions = new List<string>();
                    using (View view = database.OpenExecuteView("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`"))
                    {
                        while (true)
                        {
                            using (Record record = view.Fetch())
                            {
                                if (null == record)
                                {
                                    break;
                                }

                                string action = record.GetString(1);

                                if (!indexedSuppressedICEs.ContainsKey(action))
                                {
                                    actions.Add(action);
                                }
                            }
                        }
                    }

                    if (0 != indexedICEs.Count)
                    {
                        // Walk backwards and remove those that arent in the list
                        for (int i = actions.Count - 1; 0 <= i; i--)
                        {
                            if (!indexedICEs.ContainsKey(actions[i]))
                            {
                                actions.RemoveAt(i);
                            }
                        }
                    }

                    // disable the internal UI handler and set an external UI handler
                    previousUILevel = Installer.SetInternalUI((int)InstallUILevels.None, ref previousHwnd);
                    previousUIHandler = Installer.SetExternalUI(this.validationUIHandler, (int)InstallLogModes.Error | (int)InstallLogModes.Warning | (int)InstallLogModes.User, IntPtr.Zero);

                    // create a session for running the ICEs
                    this.validationSessionComplete = false;
                    using (Session session = new Session(database))
                    {
                        // add the product code back into the database
                        if (null != productCode)
                        {
                            // some CUBs erroneously have a ProductCode property, so delete it if we just picked one up
                            using (View dropProductCodeView = database.OpenExecuteView("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"))
                            {
                            }

                            using (View view = database.OpenExecuteView(String.Format(CultureInfo.InvariantCulture, "INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{0}')", productCode)))
                            {
                            }
                        }

                        foreach (string action in actions)
                        {
                            this.actionName = action;
                            try
                            {
                                session.DoAction(action);
                            }
                            catch (Win32Exception e)
                            {
                                if (!this.encounteredError)
                                {
                                    throw e;
                                }
                                else
                                {
                                    this.encounteredError = false;
                                }
                            }
                            this.actionName = null;
                        }

                        // Mark the validation session complete so we ignore any messages that MSI may fire
                        // during session clean-up.
                        this.validationSessionComplete = true;
                    }
                }
            }
            catch (Win32Exception e)
            {
                // avoid displaying errors twice since one may have already occurred in the UI handler
                if (!this.encounteredError)
                {
                    if (0x6E == e.NativeErrorCode) // ERROR_OPEN_FAILED
                    {
                        // databaseFile is not passed since during light
                        // this would be the temporary copy and there would be
                        // no final output since the error occured; during smoke
                        // they should know the path passed into smoke
                        this.OnMessage(WixErrors.ValidationFailedToOpenDatabase());
                    }
                    else if (0x64D == e.NativeErrorCode)
                    {
                        this.OnMessage(WixErrors.ValidationFailedDueToLowMsiEngine());
                    }
                    else if (0x654 == e.NativeErrorCode)
                    {
                        this.OnMessage(WixErrors.ValidationFailedDueToInvalidPackage());
                    }
                    else if (0x658 == e.NativeErrorCode)
                    {
                        this.OnMessage(WixErrors.ValidationFailedDueToMultilanguageMergeModule());
                    }
                    else if (0x659 == e.NativeErrorCode)
                    {
                        this.OnMessage(WixWarnings.ValidationFailedDueToSystemPolicy());
                    }
                    else
                    {
                        string msgTemp = e.Message;

                        if (null != this.actionName)
                        {
                            msgTemp = String.Concat("Action - '", this.actionName, "' ", e.Message);
                        }

                        this.OnMessage(WixErrors.Win32Exception(e.NativeErrorCode, msgTemp));
                    }
                }
            }
            finally
            {
                Installer.SetExternalUI(previousUIHandler, 0, IntPtr.Zero);
                Installer.SetInternalUI(previousUILevel, ref previousHwnd);

                this.validationSessionComplete = false; // no validation session at this point, so reset the completion flag.

                mutex.ReleaseMutex();
                this.cubeFiles.Clear();
                this.extension.FinalizeValidator();
            }

            return !this.encounteredError;
        }
Exemplo n.º 5
0
        /// <summary>
        /// Unbind an MSI database file.
        /// </summary>
        /// <param name="databaseFile">The database file.</param>
        /// <param name="database">The opened database.</param>
        /// <param name="outputType">The type of output to create.</param>
        /// <param name="exportBasePath">The path where files should be exported.</param>
        /// <param name="skipSummaryInfo">Option to skip unbinding the _SummaryInformation table.</param>
        /// <returns>The output representing the database.</returns>
        private Output UnbindDatabase(string databaseFile, Database database, OutputType outputType, string exportBasePath, bool skipSummaryInfo)
        {
            string modularizationGuid = null;
            Output output = new Output(SourceLineNumberCollection.FromFileName(databaseFile));
            View validationView = null;

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

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

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

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

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

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

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

                    output.Tables.Add(table);
                }
            }

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

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

                            string tableName = tableRecord.GetString(1);

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

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

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

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

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

                                        ColumnType columnType;
                                        int length;
                                        bool nullable;

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

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

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

                                                validationView.Execute(validationRecord);
                                            }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                                output.Tables.Add(table);
                            }

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

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

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

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

            return output;
        }
Exemplo n.º 6
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();
                    }
                }
            }
        }
Exemplo n.º 7
0
        /// <summary>
        /// Update several msi tables with data contained in files references in the File table.
        /// </summary>
        /// <remarks>
        /// For versioned files, update the file version and language in the File table.  For
        /// unversioned files, add a row to the MsiFileHash table for the file.  For assembly
        /// files, add a row to the MsiAssembly table and add AssemblyName information by adding
        /// MsiAssemblyName rows.
        /// </remarks>
        /// <param name="output">Internal representation of the msi database to operate upon.</param>
        private void UpdateFileInformation(Output output)
        {
            OutputTable mergeTable = output.OutputTables["Merge"];
            if (null != mergeTable)
            {
                foreach (OutputRow outputRow in mergeTable.OutputRows)
                {
                    MergeRow mergeRow = (MergeRow)outputRow.Row;
                    string moduleFile = null;
                    try
                    {
                        moduleFile = this.extension.FileResolutionHandler(mergeRow.SourceFile, FileResolutionType.Module);
                    }
                    catch (WixFileNotFoundException wfnfe)
                    {
                        this.OnMessage(WixErrors.BinderExtensionMissingFile(null, ErrorLevel.Normal, wfnfe.Message));
                        continue;
                    }

                    output.Modules.Add(mergeRow);
                    try
                    {
                        // read the module's File table to get its FileMediaInformation entries
                        using (Database db = new Database(moduleFile, OpenDatabase.ReadOnly))
                        {
                            mergeRow.HasFiles = false;

                            if (db.TableExists("File") && db.TableExists("Component"))
                            {
                                Hashtable uniqueModuleFileIdentifiers = System.Collections.Specialized.CollectionsUtil.CreateCaseInsensitiveHashtable();

                                using (View view = db.OpenExecuteView("SELECT `File`, `Directory_` FROM `File`, `Component` WHERE `Component_`=`Component`"))
                                {
                                    Record record;
                                    while (view.Fetch(out record))
                                    {
                                        FileMediaInformation fileMediaInformation = new FileMediaInformation(record[1], record[2], mergeRow.DiskId, String.Concat(this.tempFiles.BasePath, Path.DirectorySeparatorChar, "MergeId.", mergeRow.Id.GetHashCode().ToString("X4", CultureInfo.InvariantCulture.NumberFormat), Path.DirectorySeparatorChar, record[1]), mergeRow.Number, mergeRow.FileCompression, moduleFile, -1);
                                        FileMediaInformation otherFileMediaInformation = output.FileMediaInformationCollection[fileMediaInformation.FileId];
                                        string collidingModuleFileIdentifier = (string)uniqueModuleFileIdentifiers[fileMediaInformation.FileId];

                                        if (null == otherFileMediaInformation && null == collidingModuleFileIdentifier)
                                        {
                                            output.FileMediaInformationCollection.Add(fileMediaInformation);

                                            // keep track of file identifiers in this merge module
                                            uniqueModuleFileIdentifiers.Add(fileMediaInformation.FileId, fileMediaInformation.FileId);
                                        }
                                        else // collision(s) detected
                                        {
                                            // case-sensitive collision with another merge module or a user-authored file identifier
                                            if (null != otherFileMediaInformation)
                                            {
                                                this.OnMessage(WixErrors.DuplicateModuleFileIdentifier(mergeRow.SourceLineNumbers, mergeRow.Id, fileMediaInformation.FileId));
                                            }

                                            // case-insensitive collision with another file identifier in the same merge module
                                            if (null != collidingModuleFileIdentifier)
                                            {
                                                this.OnMessage(WixErrors.DuplicateModuleCaseInsensitiveFileIdentifier(mergeRow.SourceLineNumbers, mergeRow.Id, fileMediaInformation.FileId, collidingModuleFileIdentifier));
                                            }
                                        }

                                        mergeRow.HasFiles = true;
                                    }
                                }
                            }
                        }
                    }
                    catch (FileNotFoundException fnfe)
                    {
                        throw new WixFileNotFoundException(null, moduleFile, fnfe);
                    }
                    catch (IOException ioe)
                    {
                        throw new WixMergeModuleOpenException(mergeRow.SourceLineNumbers, mergeRow.Id, moduleFile, ioe);
                    }
                }
            }

            // calculate sequence numbers and media disk id layout for all file media information objects
            if (OutputType.Module == output.Type)
            {
                int lastSequence = 0;
                foreach (FileMediaInformation fmi in output.FileMediaInformationCollection)
                {
                    fmi.Modularize(output.ModularizationGuid);
                    fmi.Sequence = ++lastSequence;
                }
            }
            else
            {
                int lastSequence = 0;
                MediaRow mediaRow = null;
                output.FileMediaInformationCollection.Sort();
                SortedList patchGroups = new SortedList();

                // sequence the non-patch-added files
                foreach (FileMediaInformation fmi in output.FileMediaInformationCollection)
                {
                    if (null == mediaRow)
                    {
                        mediaRow = output.MediaRows[fmi.Media];
                    }
                    else if (mediaRow.DiskId != fmi.Media)
                    {
                        mediaRow.LastSequence = lastSequence;
                        mediaRow = output.MediaRows[fmi.Media];
                    }

                    if (0 < fmi.PatchGroup)
                    {
                        ArrayList patchGroup = (ArrayList)patchGroups[fmi.PatchGroup];

                        if (null == patchGroup)
                        {
                            patchGroup = new ArrayList();
                            patchGroups.Add(fmi.PatchGroup, patchGroup);
                        }

                        patchGroup.Add(fmi);
                    }
                    else
                    {
                        fmi.Sequence = ++lastSequence;
                    }
                }
                if (null != mediaRow)
                {
                    mediaRow.LastSequence = lastSequence;
                    mediaRow = null;
                }

                // sequence the patch-added files
                foreach (ArrayList patchGroup in patchGroups.Values)
                {
                    foreach (FileMediaInformation fmi in patchGroup)
                    {
                        if (null == mediaRow)
                        {
                            mediaRow = output.MediaRows[fmi.Media];
                        }
                        else if (mediaRow.DiskId != fmi.Media)
                        {
                            mediaRow.LastSequence = lastSequence;
                            mediaRow = output.MediaRows[fmi.Media];
                        }

                        fmi.Sequence = ++lastSequence;
                    }
                }
                if (null != mediaRow)
                {
                    mediaRow.LastSequence = lastSequence;
                }
            }

            // copy the special media rows back to the real media table
            if (0 < output.MediaRows.Count)
            {
                OutputTable mediaTable = Common.EnsureOutputTable(output, this.tableDefinitions["Media"]);

                foreach (MediaRow mediaRow in output.MediaRows)
                {
                    mediaTable.OutputRows.Add(new OutputRow(mediaRow));
                }
            }

            OutputTable fileTable = output.OutputTables["File"];
            if (null == fileTable)   // no work to do
            {
                return;
            }

            foreach (OutputRow outputRow in fileTable.OutputRows)
            {
                FileRow fileRow = outputRow.Row as FileRow;
                if (null == fileRow)
                {
                    throw new ApplicationException("Expected FileRow");
                }

                // copy the sequence number from the file media information to the file table
                fileRow.Sequence = output.FileMediaInformationCollection[fileRow.File].Sequence;

                string src = fileRow.Source;
                FileInfo fileInfo = null;

                if (!this.suppressFileHashAndInfo || (!this.suppressAssemblies && FileAssemblyType.NotAnAssembly != fileRow.AssemblyType))
                {
                    try
                    {
                        src = this.extension.FileResolutionHandler(fileRow.Source, FileResolutionType.File);
                    }
                    catch (WixFileNotFoundException wfnfe)
                    {
                        this.OnMessage(WixErrors.BinderExtensionMissingFile(fileRow.SourceLineNumbers, ErrorLevel.Normal, wfnfe.Message));
                        continue;
                    }

                    try
                    {
                        fileInfo = new FileInfo(src);
                    }
                    catch (ArgumentException)
                    {
                        this.OnMessage(WixErrors.InvalidFileName(fileRow.SourceLineNumbers, ErrorLevel.Normal, src));
                        continue;
                    }
                    catch (PathTooLongException)
                    {
                        this.OnMessage(WixErrors.InvalidFileName(fileRow.SourceLineNumbers, ErrorLevel.Normal, src));
                        continue;
                    }
                    catch (NotSupportedException)
                    {
                        this.OnMessage(WixErrors.InvalidFileName(fileRow.SourceLineNumbers, ErrorLevel.Normal, src));
                        continue;
                    }
                }

                if (!this.suppressFileHashAndInfo)
                {
                    if (fileInfo.Exists)
                    {
                        string version;
                        string language;

                        fileRow.FileSize = fileInfo.Length;
                        try
                        {
                            MsiBase.FileVersion(fileInfo.FullName, out version, out language);
                        }
                        catch (FileNotFoundException e)
                        {
                            throw new WixFileNotFoundException(null, fileInfo.FullName, e);   // TODO: find a way to get the sourceFile (instead of null)
                        }

                        if (0 == version.Length && 0 == language.Length)   // unversioned files have their hashes added to the MsiFileHash table
                        {
                            int[] hash;
                            try
                            {
                                MsiBase.GetFileHash(fileInfo.FullName, 0, out hash);
                            }
                            catch (FileNotFoundException e)
                            {
                                throw new WixFileNotFoundException(null, fileInfo.FullName, e);   // TODO: find a way to get the sourceFile (instead of null)
                            }

                            OutputTable outputHashTable = Common.EnsureOutputTable(output, this.tableDefinitions["MsiFileHash"]);
                            Row hashRow = new Row(outputHashTable.TableDefinition);
                            hashRow[0] = fileRow.File;
                            hashRow[1] = 0;
                            hashRow[2] = hash[0];
                            hashRow[3] = hash[1];
                            hashRow[4] = hash[2];
                            hashRow[5] = hash[3];
                            outputHashTable.OutputRows.Add(new OutputRow(hashRow));
                        }
                        else // update the file row with the version and language information
                        {
                            fileRow.Version = version;
                            fileRow.Language = language;
                        }
                    }
                    else
                    {
                        this.OnMessage(WixErrors.CannotFindFile(fileRow.SourceLineNumbers, ErrorLevel.Normal, fileRow.File, fileRow.FileName, src));
                    }
                }

                // if we're not suppressing automagically grabbing assembly information and this is a
                // CLR assembly, load the assembly and get the assembly name information
                if (!this.suppressAssemblies)
                {
                    if (FileAssemblyType.DotNetAssembly == fileRow.AssemblyType)
                    {
                        StringDictionary assemblyNameValues = new StringDictionary();

                        // under CLR 2.0, use a more robust method of gathering AssemblyName information
                        if (2 <= Environment.Version.Major)
                        {
                            CLRInterop.IReferenceIdentity referenceIdentity = null;
                            Guid referenceIdentityGuid = CLRInterop.ReferenceIdentityGuid;

                            if (0 == CLRInterop.GetAssemblyIdentityFromFile(fileInfo.FullName, ref referenceIdentityGuid, out referenceIdentity))
                            {
                                if (null != referenceIdentity)
                                {
                                    string culture = referenceIdentity.GetAttribute(null, "Culture");
                                    if (null != culture)
                                    {
                                        assemblyNameValues.Add("Culture", culture);
                                    }

                                    string name = referenceIdentity.GetAttribute(null, "Name");
                                    if (null != name)
                                    {
                                        assemblyNameValues.Add("Name", name);
                                    }

                                    string processorArchitecture = referenceIdentity.GetAttribute(null, "ProcessorArchitecture");
                                    if (null != processorArchitecture)
                                    {
                                        assemblyNameValues.Add("ProcessorArchitecture", processorArchitecture);
                                    }

                                    string publicKeyToken = referenceIdentity.GetAttribute(null, "PublicKeyToken");
                                    if (null != publicKeyToken)
                                    {
                                        assemblyNameValues.Add("PublicKeyToken", publicKeyToken.ToUpper(CultureInfo.InvariantCulture));
                                    }

                                    string version = referenceIdentity.GetAttribute(null, "Version");
                                    if (null != version)
                                    {
                                        assemblyNameValues.Add("Version", version);
                                    }
                                }
                            }
                        }
                        else
                        {
                            AssemblyName assemblyName = null;
                            try
                            {
                                assemblyName = AssemblyName.GetAssemblyName(fileInfo.FullName);

                                if (null != assemblyName.CultureInfo)
                                {
                                    assemblyNameValues.Add("Culture", assemblyName.CultureInfo.ToString());
                                }

                                if (null != assemblyName.Name)
                                {
                                    assemblyNameValues.Add("Name", assemblyName.Name);
                                }

                                byte[] publicKey = assemblyName.GetPublicKeyToken();
                                if (null != publicKey && 0 < publicKey.Length)
                                {
                                    StringBuilder sb = new StringBuilder();
                                    for (int i = 0; i < publicKey.GetLength(0); ++i)
                                    {
                                        sb.AppendFormat("{0:X2}", publicKey[i]);
                                    }
                                    assemblyNameValues.Add("PublicKeyToken", sb.ToString());
                                }

                                if (null != assemblyName.Version)
                                {
                                    assemblyNameValues.Add("Version", assemblyName.Version.ToString());
                                }
                            }
                            catch (FileNotFoundException fnfe)
                            {
                                throw new WixFileNotFoundException(fileRow.SourceLineNumbers, fileInfo.FullName, fnfe);
                            }
                            catch (Exception e)
                            {
                                if (e is NullReferenceException || e is SEHException)
                                {
                                    throw;
                                }
                                else
                                {
                                    throw new WixInvalidAssemblyException(fileRow.SourceLineNumbers, fileInfo, e);
                                }
                            }
                        }

                        OutputTable assemblyNameOutputTable = Common.EnsureOutputTable(output, this.tableDefinitions["MsiAssemblyName"]);
                        if (assemblyNameValues.ContainsKey("name"))
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "name", assemblyNameValues["name"]);
                        }

                        string fileVersion = null;
                        if (this.setMsiAssemblyNameFileVersion)
                        {
                            string language;

                            MsiBase.FileVersion(fileInfo.FullName, out fileVersion, out language);
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "fileVersion", fileVersion);
                        }

                        if (assemblyNameValues.ContainsKey("version"))
                        {
                            string assemblyVersion = assemblyNameValues["version"];

                            // there is a bug in fusion that requires the assembly's "version" attribute
                            // to be equal to or longer than the "fileVersion" in length when its present;
                            // the workaround is to prepend zeroes to the last version number in the assembly version
                            if (this.setMsiAssemblyNameFileVersion && null != fileVersion && fileVersion.Length > assemblyVersion.Length)
                            {
                                string padding = new string('0', fileVersion.Length - assemblyVersion.Length);
                                string[] assemblyVersionNumbers = assemblyVersion.Split('.');

                                if (assemblyVersionNumbers.Length > 0)
                                {
                                    assemblyVersionNumbers[assemblyVersionNumbers.Length - 1] = String.Concat(padding, assemblyVersionNumbers[assemblyVersionNumbers.Length - 1]);
                                    assemblyVersion = String.Join(".", assemblyVersionNumbers);
                                }
                            }

                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "version", assemblyVersion);
                        }

                        if (assemblyNameValues.ContainsKey("culture"))
                        {
                            string culture = assemblyNameValues["culture"];
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "culture", (String.Empty == culture ? "neutral" : culture));
                        }

                        if (assemblyNameValues.ContainsKey("publicKeyToken"))
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "publicKeyToken", assemblyNameValues["publicKeyToken"]);
                        }

                        if (null != fileRow.ProcessorArchitecture && 0 < fileRow.ProcessorArchitecture.Length)
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "processorArchitecture", fileRow.ProcessorArchitecture);
                        }

                        if (assemblyNameValues.ContainsKey("processorArchitecture"))
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "processorArchitecture", assemblyNameValues["processorArchitecture"]);
                        }
                    }
                    else if (FileAssemblyType.Win32Assembly == fileRow.AssemblyType)
                    {
                        FileRow fileManifestRow = fileRow;

                        // would rather look this up through a data structure rather than
                        // do an order n search through the list of files for every
                        // instance of a win32 assembly.  From what I can find, there
                        // are no indexed data structures available at this point
                        // in the code we're left with this expensive search.
                        foreach (OutputRow manifestOutputRow in fileTable.OutputRows)
                        {
                            fileManifestRow = manifestOutputRow.Row as FileRow;
                            if (fileManifestRow.File == fileRow.AssemblyManifest)
                            {
                                break;
                            }
                        }

                        string type = null;
                        string name = null;
                        string version = null;
                        string processorArchitecture = null;
                        string publicKeyToken = null;

                        // loading the dom is expensive we want more performant APIs than the DOM
                        // Navigator is cheaper than dom.  Perhaps there is a cheaper API still.
                        string manifestSourcePath = null;
                        try
                        {
                            manifestSourcePath = this.extension.FileResolutionHandler(fileManifestRow.Source, FileResolutionType.File);
                        }
                        catch (WixFileNotFoundException wfnfe)
                        {
                            this.OnMessage(WixErrors.BinderExtensionMissingFile(fileRow.SourceLineNumbers, ErrorLevel.Normal, wfnfe.Message));
                            continue;
                        }

                        try
                        {
                            XPathDocument doc = new XPathDocument(manifestSourcePath);
                            XPathNavigator nav = doc.CreateNavigator();
                            nav.MoveToRoot();

                            // this assumes a particular schema for a win32 manifest and does not
                            // provide error checking if the file does not conform to schema.
                            // The fallback case here is that nothing is added to the MsiAssemblyName
                            // table for a out of tollerence Win32 manifest.  Perhaps warnings needed.
                            if (nav.MoveToFirstChild())
                            {
                                while (nav.NodeType != XPathNodeType.Element || nav.Name != "assembly")
                                {
                                    nav.MoveToNext();
                                }
                                if (nav.MoveToFirstChild())
                                {
                                    while (nav.NodeType != XPathNodeType.Element || nav.Name != "assemblyIdentity")
                                    {
                                        nav.MoveToNext();
                                    }
                                    if (nav.MoveToAttribute("type", String.Empty))
                                    {
                                        type = nav.Value;
                                        nav.MoveToParent();
                                    }
                                    if (nav.MoveToAttribute("name", String.Empty))
                                    {
                                        name = nav.Value;
                                        nav.MoveToParent();
                                    }
                                    if (nav.MoveToAttribute("version", String.Empty))
                                    {
                                        version = nav.Value;
                                        nav.MoveToParent();
                                    }
                                    if (nav.MoveToAttribute("processorArchitecture", String.Empty))
                                    {
                                        processorArchitecture = nav.Value;
                                        nav.MoveToParent();
                                    }
                                    if (nav.MoveToAttribute("publicKeyToken", String.Empty))
                                    {
                                        publicKeyToken = nav.Value;
                                        nav.MoveToParent();
                                    }
                                }
                            }
                        }
                        catch (XmlException xe)
                        {
                            this.OnMessage(WixErrors.InvalidXml(SourceLineNumberCollection.FromFileName(manifestSourcePath), "manifest", xe.Message));
                        }

                        OutputTable assemblyNameOutputTable = Common.EnsureOutputTable(output, this.tableDefinitions["MsiAssemblyName"]);
                        if (null != name && 0 < name.Length)
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "name", name);
                        }
                        if (null != version && 0 < version.Length)
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "version", version);
                        }
                        if (null != type && 0 < type.Length)
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "type", type);
                        }
                        if (null != processorArchitecture && 0 < processorArchitecture.Length)
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "processorArchitecture", processorArchitecture);
                        }
                        if (null != publicKeyToken && 0 < publicKeyToken.Length)
                        {
                            this.SetMsiAssemblyName(assemblyNameOutputTable, fileRow, "publicKeyToken", publicKeyToken);
                        }
                    }
                }
            }
        }
Exemplo n.º 8
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();
            }
        }
Exemplo n.º 9
0
        /// <summary>
        /// Updates database with signatures from external cabinets.
        /// </summary>
        /// <param name="databaseFile">Path to MSI database.</param>
        /// <param name="outputFile">Ouput for updated MSI database.</param>
        /// <param name="tidy">Clean up files.</param>
        /// <returns>True if database is updated.</returns>
        public bool InscribeDatabase(string databaseFile, string outputFile, bool tidy)
        {
            // Keeps track of whether we've encountered at least one signed cab or not - we'll throw a warning if no signed cabs were encountered
            bool foundUnsignedExternals = false;
            bool shouldCommit = false;

            FileAttributes attributes = File.GetAttributes(databaseFile);
            if (FileAttributes.ReadOnly == (attributes & FileAttributes.ReadOnly))
            {
                this.OnMessage(WixErrors.ReadOnlyOutputFile(databaseFile));
                return shouldCommit;
            }

            using (Database database = new Database(databaseFile, OpenDatabase.Transact))
            {
                // Just use the English codepage, because the tables we're importing only have binary streams / MSI identifiers / other non-localizable content
                int codepage = 1252;

                // list of certificates for this database (hash/identifier)
                Dictionary<string, string> certificates = new Dictionary<string, string>();

                // Reset the in-memory tables for this new database
                Table digitalSignatureTable = new Table(null, this.tableDefinitions["MsiDigitalSignature"]);
                Table digitalCertificateTable = new Table(null, this.tableDefinitions["MsiDigitalCertificate"]);

                // If any digital signature records exist that are not of the media type, preserve them
                if (database.TableExists("MsiDigitalSignature"))
                {
                    using (View digitalSignatureView = database.OpenExecuteView("SELECT `Table`, `SignObject`, `DigitalCertificate_`, `Hash` FROM `MsiDigitalSignature` WHERE `Table` <> 'Media'"))
                    {
                        while (true)
                        {
                            using (Record digitalSignatureRecord = digitalSignatureView.Fetch())
                            {
                                if (null == digitalSignatureRecord)
                                {
                                    break;
                                }

                                Row digitalSignatureRow = null;
                                digitalSignatureRow = digitalSignatureTable.CreateRow(null);

                                string table = digitalSignatureRecord.GetString(0);
                                string signObject = digitalSignatureRecord.GetString(1);

                                digitalSignatureRow[0] = table;
                                digitalSignatureRow[1] = signObject;
                                digitalSignatureRow[2] = digitalSignatureRecord.GetString(2);

                                if (false == digitalSignatureRecord.IsNull(3))
                                {
                                    // Export to a file, because the MSI API's require us to provide a file path on disk
                                    string hashPath = Path.Combine(this.TempFilesLocation, "MsiDigitalSignature");
                                    string hashFileName = string.Concat(table,".", signObject, ".bin");

                                    Directory.CreateDirectory(hashPath);
                                    hashPath = Path.Combine(hashPath, hashFileName);

                                    using (FileStream fs = File.Create(hashPath))
                                    {
                                        int bytesRead;
                                        byte[] buffer = new byte[1024 * 4];

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

                                    digitalSignatureRow[3] = hashFileName;
                                }
                            }
                        }
                    }
                }

                // If any digital certificates exist, extract and preserve them
                if (database.TableExists("MsiDigitalCertificate"))
                {
                    using (View digitalCertificateView = database.OpenExecuteView("SELECT * FROM `MsiDigitalCertificate`"))
                    {
                        while (true)
                        {
                            using (Record digitalCertificateRecord = digitalCertificateView.Fetch())
                            {
                                if (null == digitalCertificateRecord)
                                {
                                    break;
                                }

                                string certificateId = digitalCertificateRecord.GetString(1); // get the identifier of the certificate

                                // Export to a file, because the MSI API's require us to provide a file path on disk
                                string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
                                Directory.CreateDirectory(certPath);
                                certPath = Path.Combine(certPath, string.Concat(certificateId, ".cer"));

                                using (FileStream fs = File.Create(certPath))
                                {
                                    int bytesRead;
                                    byte[] buffer = new byte[1024 * 4];

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

                                // Add it to our "add to MsiDigitalCertificate" table dictionary
                                Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
                                digitalCertificateRow[0] = certificateId;

                                // Now set the file path on disk where this binary stream will be picked up at import time
                                digitalCertificateRow[1] = string.Concat(certificateId, ".cer");

                                // Load the cert to get it's thumbprint
                                X509Certificate cert = X509Certificate.CreateFromCertFile(certPath);
                                X509Certificate2 cert2 = new X509Certificate2(cert);

                                certificates.Add(cert2.Thumbprint, certificateId);
                            }
                        }
                    }
                }

                using (View mediaView = database.OpenExecuteView("SELECT * FROM `Media`"))
                {
                    while (true)
                    {
                        using (Record mediaRecord = mediaView.Fetch())
                        {
                            if (null == mediaRecord)
                            {
                                break;
                            }

                            X509Certificate2 cert2 = null;
                            Row digitalSignatureRow = null;

                            string cabName = mediaRecord.GetString(4); // get the name of the cab
                            // If there is no cabinet or it's an internal cab, skip it.
                            if (String.IsNullOrEmpty(cabName) || cabName.StartsWith("#", StringComparison.Ordinal))
                            {
                                continue;
                            }

                            string cabId = mediaRecord.GetString(1); // get the ID of the cab
                            string cabPath = Path.Combine(Path.GetDirectoryName(databaseFile), cabName);

                            // If the cabs aren't there, throw an error but continue to catch the other errors
                            if (!File.Exists(cabPath))
                            {
                                this.OnMessage(WixErrors.WixFileNotFound(cabPath));
                                continue;
                            }

                            try
                            {
                                // Get the certificate from the cab
                                X509Certificate signedFileCert = X509Certificate.CreateFromSignedFile(cabPath);
                                cert2 = new X509Certificate2(signedFileCert);
                            }
                            catch (System.Security.Cryptography.CryptographicException e)
                            {
                                uint HResult = unchecked((uint)Marshal.GetHRForException(e));

                                // If the file has no cert, continue, but flag that we found at least one so we can later give a warning
                                if (0x80092009 == HResult) // CRYPT_E_NO_MATCH
                                {
                                    foundUnsignedExternals = true;
                                    continue;
                                }

                                // todo: exactly which HRESULT corresponds to this issue?
                                // If it's one of these exact platforms, warn the user that it may be due to their OS.
                                if ((5 == Environment.OSVersion.Version.Major && 2 == Environment.OSVersion.Version.Minor) || // W2K3
                                        (5 == Environment.OSVersion.Version.Major && 1 == Environment.OSVersion.Version.Minor)) // XP
                                {
                                    this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFileDownlevelOS(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
                                }
                                else // otherwise, generic error
                                {
                                    this.OnMessage(WixErrors.UnableToGetAuthenticodeCertOfFile(cabPath, String.Format(CultureInfo.InvariantCulture, "HRESULT: 0x{0:x8}", HResult)));
                                }
                            }

                            // If we haven't added this cert to the MsiDigitalCertificate table, set it up to be added
                            if (!certificates.ContainsKey(cert2.Thumbprint))
                            {
                                // generate a stable identifier
                                string certificateGeneratedId = Common.GenerateIdentifier("cer", true, cert2.Thumbprint);

                                // Add it to our "add to MsiDigitalCertificate" table dictionary
                                Row digitalCertificateRow = digitalCertificateTable.CreateRow(null);
                                digitalCertificateRow[0] = certificateGeneratedId;

                                // Export to a file, because the MSI API's require us to provide a file path on disk
                                string certPath = Path.Combine(this.TempFilesLocation, "MsiDigitalCertificate");
                                Directory.CreateDirectory(certPath);
                                certPath = Path.Combine(certPath, string.Concat(cert2.Thumbprint, ".cer"));
                                File.Delete(certPath);

                                using (BinaryWriter writer = new BinaryWriter(File.Open(certPath, FileMode.Create)))
                                {
                                    writer.Write(cert2.RawData);
                                    writer.Close();
                                }

                                // Now set the file path on disk where this binary stream will be picked up at import time
                                digitalCertificateRow[1] = string.Concat(cert2.Thumbprint, ".cer");

                                certificates.Add(cert2.Thumbprint, certificateGeneratedId);
                            }

                            digitalSignatureRow = digitalSignatureTable.CreateRow(null);

                            digitalSignatureRow[0] = "Media";
                            digitalSignatureRow[1] = cabId;
                            digitalSignatureRow[2] = certificates[cert2.Thumbprint];
                        }
                    }
                }

                if (digitalCertificateTable.Rows.Count > 0)
                {
                    database.ImportTable(codepage, (IMessageHandler)this, digitalCertificateTable, this.TempFilesLocation, true);
                    shouldCommit = true;
                }

                if (digitalSignatureTable.Rows.Count > 0)
                {
                    database.ImportTable(codepage, (IMessageHandler)this, digitalSignatureTable, this.TempFilesLocation, true);
                    shouldCommit = true;
                }

                // TODO: if we created the table(s), then we should add the _Validation records for them.

                certificates = null;

                // If we did find external cabs but none of them were signed, give a warning
                if (foundUnsignedExternals)
                {
                    this.OnMessage(WixWarnings.ExternalCabsAreNotSigned(databaseFile));
                }

                if (shouldCommit)
                {
                    database.Commit();
                }
            }

            return shouldCommit;
        }