Example #1
1
File: Binder.cs Project: 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();
            }
        }
Example #2
0
        internal static MsiPropertyInfo[] GetPropertiesFromDatabase(Database msidb)
        {
            int[] standardIDs = new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 18, 19};

            ArrayList properties = new ArrayList();
            using (SummaryInformation summaryInfo = new SummaryInformation(msidb))
            {
                foreach (int propID in standardIDs)
                {
                    bool failed = false;
                    object propValue = null;
                    try
                    {
                        propValue = summaryInfo.GetProperty(propID);
                    }
                    catch
                    {
                        failed = true;
                    }
                    if (!failed)
                        properties.Add(new MsiPropertyInfo(propID, propValue));
                }
            }
            return (MsiPropertyInfo[]) properties.ToArray(typeof (MsiPropertyInfo));
        }
Example #3
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));
            }
        }
Example #4
0
		/// <summary>
		/// Creates a list of <see cref="MsiFile"/> objects from the specified database.
		/// </summary>
		public static MsiFile[] CreateMsiFilesFromMSI(string msiDatabaseFilePath)
		{
			using (var db = new Database(msiDatabaseFilePath, OpenDatabase.ReadOnly))
			{
				return CreateMsiFilesFromMSI(db);
			}
		}
Example #5
0
 public override void Run(List<string> args)
 {
     // args[0]=v, args[1]=filename.msi
     if (args.Count < 2)
         throw new OptionException("You must specify an msi filename.", "v");
     var msiFileName = args[1];
     using (var msidb = new Database(msiFileName, OpenDatabase.ReadOnly))
     {
         const string tableName = "Property";
         var query = string.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName);
         using (var view = new ViewWrapper(msidb.OpenExecuteView(query)))
         {
             foreach (var row in view.Records)
             {
                 var property = (string)row[view.ColumnIndex("Property")];
                 var value = row[view.ColumnIndex("Value")];
                 if (string.Equals("ProductVersion", property, StringComparison.InvariantCultureIgnoreCase))
                 {
                     Console.WriteLine(value);
                     return;
                 }
             }
             Console.WriteLine("Version not found!");
         }
     }
 }
Example #6
0
        public override void Run(List<string> args)
        {
            /* examples:
             *	lessmsi l -t Component c:\theinstall.msi
             *	lessmsi l -t Property c:\theinstall.msi
            */
            args = args.Skip(1).ToList();
            var tableName = "";
            var options = new OptionSet {
                { "t=", "Specifies the table to list.", t => tableName = t }
            };
            var extra = options.Parse(args);
            if (extra.Count < 1)
                throw new OptionException("You must specify the msi file to list from.", "l");
            if (string.IsNullOrEmpty(tableName))
                throw new OptionException("You must specify the table name to list.", "t");

            var csv = new StringBuilder();
            Debug.Print("Opening msi file '{0}'.", extra[0]);
            using (var msidb = new Database(extra[0], OpenDatabase.ReadOnly))
            {
                Debug.Print("Opening table '{0}'.", tableName);
                var query = string.Format(CultureInfo.InvariantCulture, "SELECT * FROM `{0}`", tableName);
                using (var view = new ViewWrapper(msidb.OpenExecuteView(query)))
                {
                    for (var index = 0; index < view.Columns.Length; index++)
                    {
                        var col = view.Columns[index];
                        if (index > 0)
                            csv.Append(',');
                        csv.Append(col.Name);
                    }
                    csv.AppendLine();
                    foreach (var row in view.Records)
                    {
                        for (var colIndex = 0; colIndex < row.Length; colIndex++)
                        {
                            if (colIndex > 0)
                                csv.Append(',');
                            var val = Convert.ToString(row[colIndex], CultureInfo.InvariantCulture);
                            var newLine = Environment.NewLine;
                            string[] requireEscapeChars = { ",", newLine };
                            Array.ForEach(requireEscapeChars, s => {
                                if (val.Contains(s))
                                    val = "\"" + val + "\"";
                            });
                            csv.Append(val);
                        }
                        csv.AppendLine();
                    }
                }
            }
            Console.Write(csv.ToString());
        }
Example #7
0
        /// <summary>
        /// Instantiate a new Session.
        /// </summary>
        /// <param name="database">The database to open.</param>
        public Session(Database database)
        {
            string packagePath = String.Format(CultureInfo.InvariantCulture, "#{0}", (uint)database.Handle);

            uint handle = 0;
            int error = MsiInterop.MsiOpenPackage(packagePath, out handle);
            if (0 != error)
            {
                throw new MsiException(error);
            }
            this.Handle = handle;
        }
        /// <summary>
        /// Instantiate a new SummaryInformation class from the database.
        /// </summary>
        /// <param name="db">Database to retrieve summary information from.</param>
        public SummaryInformation(Database db)
        {
            if (null == db)
            {
                throw new ArgumentNullException("db");
            }

            uint error = MsiInterop.MsiGetSummaryInformation(db.InternalHandle, null, 20, ref handle);
            if (0 != error)
            {
                throw new ArgumentNullException();   // TODO: come up with a real exception to throw
            }
        }
Example #9
0
        /// <summary>
        /// Creates a list of <see cref="MsiFile"/> objects from the specified database.
        /// </summary>
        public static MsiFile[] CreateMsiFilesFromMSI(Database msidb)
        {
            TableRow[] rows = TableRow.GetRowsFromTable(msidb, "File");

            // do some prep work to cache values from MSI for finding directories later...
            MsiDirectory[] rootDirectories;
            MsiDirectory[] allDirectories;
            MsiDirectory.GetMsiDirectories(msidb, out rootDirectories, out allDirectories);

            //find the target directory for each by reviewing the Component Table
            TableRow[] components = TableRow.GetRowsFromTable(msidb, "Component"); //Component table: http://msdn.microsoft.com/en-us/library/aa368007(v=vs.85).aspx
            //build a table of components keyed by it's "Component" column value
            Hashtable componentsByComponentTable = new Hashtable();
            foreach (TableRow component in components)
            {
                componentsByComponentTable[component.GetString("Component")] = component;
            }

            ArrayList/*<MsiFile>*/ files = new ArrayList(rows.Length);
            foreach (TableRow row in rows)
            {
                MsiFile file = new MsiFile();
				
                string fileName = row.GetString("FileName");
                string[] split = fileName.Split('|');
                file.ShortFileName = split[0];
                if (split.Length > 1)
                    file.LongFileName = split[1];
                else
                    file.LongFileName = split[0];

                file.File = row.GetString("File");
                file.FileSize = row.GetInt32("FileSize");
                file.Version = row.GetString("Version");
                file.Component = row.GetString("Component_");

                file._directory = GetDirectoryForFile(file, allDirectories, componentsByComponentTable);
                files.Add(file);
            }
            return (MsiFile[])files.ToArray(typeof(MsiFile));
        }
Example #10
0
        /// <summary>
        /// Write the Cab to disk.
        /// </summary>
        /// <param name="filePath">Specifies the path to the file to contain the stream.</param>
        /// <param name="cabName">Specifies the name of the file in the stream.</param>
        public static void ExtractCabFromPackage(string filePath, string cabName, Database inputDatabase)
        {
            using (View view = inputDatabase.OpenExecuteView(String.Concat("SELECT * FROM `_Streams` WHERE `Name` = '", cabName, "'")))
            {
                Record record;
                if (view.Fetch(out record))
                {
                    FileStream cabFilestream = null;
                    BinaryWriter writer = null;
                    try
                    {
                        cabFilestream = new FileStream(filePath, FileMode.Create);

                        // Create the writer for data.
                        writer = new BinaryWriter(cabFilestream);

                        var buf = new byte[1024*1024];
                        int count;
                        do
                        {
                            const int MsiInterop_Storages_Data = 2; //From wiX:Index to column name Data into Record for row in Msi Table Storages
                            count = record.GetStream(MsiInterop_Storages_Data, buf, buf.Length);
                            if (count > 0)
                                writer.Write(buf, 0, count);
                        } while (count > 0);
                    }
                    finally
                    {
                        if (writer != null)
                        {
                            writer.Close();
                        }

                        if (cabFilestream != null)
                        {
                            cabFilestream.Close();
                        }
                    }
                }
            }
        }
Example #11
0
 /// <summary>
 /// Creates and populates the summary information stream of an existing transform file.
 /// </summary>
 /// <param name="referenceDatabase">Required database that does not include the changes.</param>
 /// <param name="transformFile">The name of the generated transform file.</param>
 /// <param name="errorConditions">Required error conditions that should be suppressed when the transform is applied.</param>
 /// <param name="validations">Required when the transform is applied to a database;
 /// shows which properties should be validated to verify that this transform can be applied to the database.</param>
 public void CreateTransformSummaryInfo(Database referenceDatabase, string transformFile, TransformErrorConditions errorConditions, TransformValidations validations)
 {
     int error = MsiInterop.MsiCreateTransformSummaryInfo(this.Handle, referenceDatabase.Handle, transformFile, errorConditions, validations);
     if (0 != error)
     {
         throw new MsiException(error);
     }
 }
Example #12
0
        /// <summary>
        /// Extracts the compressed files from the specified MSI file to the specified output directory.
        /// If specified, the list of <paramref name="filesToExtract"/> objects are the only files extracted.
        /// </summary>
        /// <param name="filesToExtract">The files to extract or null or empty to extract all files.</param>
        /// <param name="progressCallback">Will be called during during the operation with progress information, and upon completion. The argument will be of type <see cref="ExtractionProgress"/>.</param>
        public static void ExtractFiles(FileInfo msi, DirectoryInfo outputDir, MsiFile[] filesToExtract, AsyncCallback progressCallback)
        {
            if (msi == null)
                throw new ArgumentNullException("msi");
            if (outputDir == null)
                throw new ArgumentNullException("outputDir");

            int filesExtractedSoFar = 0;

            ExtractionProgress progress = null;
            Database msidb = new Database(msi.FullName, OpenDatabase.ReadOnly);
            try
            {
                if (filesToExtract == null || filesToExtract.Length < 1)
                    filesToExtract = MsiFile.CreateMsiFilesFromMSI(msidb);

                progress = new ExtractionProgress(progressCallback, filesToExtract.Length);

                if (!msi.Exists)
                {
                    Trace.WriteLine("File \'" + msi.FullName + "\' not found.");
                    progress.ReportProgress(ExtractionActivity.Complete, "", filesExtractedSoFar);
                    return;
                }

                progress.ReportProgress(ExtractionActivity.Initializing, "", filesExtractedSoFar);
                outputDir.Create();

                //map short file names to the msi file entry
                var fileEntryMap = new Dictionary<string, MsiFile>(filesToExtract.Length, StringComparer.InvariantCulture);
                foreach (var fileEntry in filesToExtract)
                {
                    MsiFile existingFile = null;
                    if (fileEntryMap.TryGetValue(fileEntry.File, out existingFile))
                    {	//NOTE: This used to be triggered when we ignored case of file, but now we don't ignore case so this is unlikely to occur.
                        // Differing only by case is not compliant with the msi specification but some installers do it (e.g. python, see issue 28).
                        Debug.Print("!!Found duplicate file using key {0}. The existing key was {1}", fileEntry.File, existingFile.File);
                    }
                    else
                    {
                        fileEntryMap.Add(fileEntry.File, fileEntry);
                    }
                }

                Debug.Assert(fileEntryMap.Count == filesToExtract.Length, "Duplicate files must have caused some files to not be in the map.");

                var cabInfos = CabsFromMsiToDisk(msidb, outputDir);
                var cabDecompressors = MergeCabs(cabInfos);
                try
                {
                    foreach (MSCabinet decompressor in cabDecompressors)
                    {
                        foreach (var compressedFile in decompressor.GetFiles())
                        {
                            // if the user didn't select this in the UI for extraction, skip it.
                            if (!fileEntryMap.ContainsKey(compressedFile.Filename))
                                continue;
                            var entry = fileEntryMap[compressedFile.Filename];
                            progress.ReportProgress(ExtractionActivity.ExtractingFile, entry.LongFileName, filesExtractedSoFar);
                            DirectoryInfo targetDirectoryForFile = GetTargetDirectory(outputDir, entry.Directory);
                            string destName = Path.Combine(targetDirectoryForFile.FullName, entry.LongFileName);
                            if (File.Exists(destName))
                            {
                                Debug.Fail("output file already exists. We'll make it unique, but this is probably a strange msi or a bug in this program.");
                                //make unique
                                // ReSharper disable HeuristicUnreachableCode
                                Trace.WriteLine(string.Concat("Duplicate file found \'", destName, "\'"));
                                int duplicateCount = 0;
                                string uniqueName;
                                do
                                {
                                    uniqueName = string.Concat(destName, ".", "duplicate", ++duplicateCount);
                                } while (File.Exists(uniqueName));
                                destName = uniqueName;
                                // ReSharper restore HeuristicUnreachableCode
                            }
                            Trace.WriteLine(string.Concat("Extracting File \'", compressedFile.Filename, "\' to \'", destName, "\'"));
                            compressedFile.ExtractTo(destName);
                            filesExtractedSoFar++;
                        }
                    }
                }
                finally
                {	//cleanup the decompressors allocated in MergeCabs
                    foreach (MSCabinet decomp in cabDecompressors)
                    {
                        decomp.Close(false);
                        File.Delete(decomp.LocalFilePath);
                    }
                }
            }
            finally
            {
                if (msidb != null)
                    msidb.Close();
                if (progress != null)
                    progress.ReportProgress(ExtractionActivity.Complete, "", filesExtractedSoFar);
            }
        }
Example #13
0
        /// <summary>
        /// Extracts cab files from the specified MSIDB and puts them in the specified outputdir.
        /// </summary>
        /// <param name="msidb"></param>
        /// <param name="outputDir"></param>
        /// <returns></returns>
        private static List<CabInfo> CabsFromMsiToDisk(Database msidb, DirectoryInfo outputDir)
        {
            const string query = "SELECT * FROM `Media`";
            var localCabFiles = new List<CabInfo>();
            using (View view = msidb.OpenExecuteView(query))
            {
                Record record;
                while (view.Fetch(out record))
                {
                    const int MsiInterop_Media_Cabinet = 4;
                    string cabSourceName = record[MsiInterop_Media_Cabinet];
                    if (string.IsNullOrEmpty(cabSourceName))
                        throw new IOException("Couldn't find media CAB file inside the MSI (bad media table?).");
                    if (!string.IsNullOrEmpty(cabSourceName))
                    {
                        if (cabSourceName.StartsWith("#"))
                        {
                            cabSourceName = cabSourceName.Substring(1);

                            // extract cabinet, then explode all of the files to a temp directory
                            string localCabFile = Path.Combine(outputDir.FullName, cabSourceName);

                            ExtractCabFromPackage(localCabFile, cabSourceName, msidb);
                            /* http://code.google.com/p/lessmsi/issues/detail?id=1
                     		 * apparently in some cases a file spans multiple CABs (VBRuntime.msi) so due to that we have get all CAB files out of the MSI and then begin extraction. Then after we extract everything out of all CAbs we need to release the CAB extractors and delete temp files.
                             * Thanks to Christopher Hamburg for explaining this!
                     		*/
                            var c = new CabInfo(localCabFile, cabSourceName);
                            localCabFiles.Add(c);
                        }
                    }
                }
            }
            return localCabFiles;
        }
Example #14
0
File: Binder.cs Project: 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();
                            }
                        }
                    }
                }
            }
        }
Example #15
0
        /// <summary>
        /// Creates a list of <see cref="MsiDirectory"/> objects from the specified database.
        /// </summary>
        /// <param name="allDirectories">All directories in the table.</param>
        /// <param name="msidb">The databse to get directories from.</param>
        /// <param name="rootDirectories">
        /// Only the root directories (those with no parent). Use <see cref="MsiDirectory.Children"/> to traverse the rest of the directories.
        /// </param>
        public static void GetMsiDirectories(Database msidb, out MsiDirectory[] rootDirectories, out MsiDirectory[] allDirectories)
        {
            TableRow[] rows = TableRow.GetRowsFromTable(msidb, "Directory");
            Hashtable directoriesByDirID = new Hashtable();

            foreach (TableRow row in rows)
            {
                MsiDirectory directory = new MsiDirectory();
                directory._defaultDir = row.GetString("DefaultDir");
                if (directory._defaultDir != null && directory._defaultDir.Length > 0)
                {
                    string[] split = directory._defaultDir.Split('|');

                    directory._shortName = split[0];
                    if (split.Length > 1)
                        directory._targetName = split[1];
                    else
                        directory._targetName = split[0];
                    
                    //Semi colons can delmit the "target" and "sorce" names of the directory in DefaultDir, so we're going to use the Target here (in looking at MSI files, I found Target seems most meaningful.
                    #region MSDN Docs on this Table
                    /*  From: http://msdn.microsoft.com/en-us/library/aa368295%28VS.85%29.aspx
                    The DefaultDir column contains the directory's name (localizable)under the parent directory. 
                    By default, this is the name of both the target and source directories. 
                    To specify different source and target directory names, separate the target and source names with a colon as follows: [targetname]:[sourcename].
                    If the value of the Directory_Parent column is null or is equal to the Directory column, the DefaultDir column specifies the name of a root source directory.
                    For a non-root source directory, a period (.) entered in the DefaultDir column for the source directory name or the target directory name indicates the directory should be located in its parent directory without a subdirectory.
                    The directory names in this column may be formatted as short filename | long filename pairs.
                    */
                    #endregion
                    split = directory._shortName.Split(':');
                    if (split.Length > 1)
                    {   //semicolon present
                        directory._shortName = split[0];
                    }
                    split = directory._targetName.Split(':');
                    if (split.Length > 1)
                    {   //semicolon present
                        directory._targetName = split[0];
                        directory._sourceName = split[1];
                    }
                    else
                    {
                        directory._sourceName = directory._targetName;
                    }
                }
                
                directory._directory = row.GetString("Directory");
                directory._directoryParent = row.GetString("Directory_Parent");
                directoriesByDirID.Add(directory.Directory, directory);
            }
            //Now we have all directories in the table, create a structure for them based on their parents.
            ArrayList rootDirectoriesList = new ArrayList();
            foreach (MsiDirectory dir in directoriesByDirID.Values)
            {
                if (dir.DirectoryParent == null || dir.DirectoryParent.Length == 0)
                {
                    rootDirectoriesList.Add(dir);
                    continue;
                }

                MsiDirectory parent = directoriesByDirID[dir.DirectoryParent] as MsiDirectory;
                dir._parent = parent;
                parent._children.Add(dir);
            }
            // return the values:
            rootDirectories = (MsiDirectory[])rootDirectoriesList.ToArray(typeof(MsiDirectory));
			
            MsiDirectory[] allDirectoriesLocal = new MsiDirectory[directoriesByDirID.Values.Count];
            directoriesByDirID.Values.CopyTo(allDirectoriesLocal,0);
            allDirectories = allDirectoriesLocal;
        }
Example #16
0
        /// <summary>
        /// Displays the list of files in the extract tab.
        /// </summary>
        /// <param name="msidb">The msi database.</param>
        private void ViewFiles(Database msidb)
        {
            if (msidb == null)
                return;

            using (new DisposableCursor(View))
            {
                try
                {
                    Status("");

                    MsiFile[] dataItems = MsiFile.CreateMsiFilesFromMSI(msidb);
                    MsiFileItemView[] viewItems = Array.ConvertAll<MsiFile, MsiFileItemView>(dataItems,
                        inItem => new MsiFileItemView(inItem)
                        );
                    var fileDataSource = new SortableBindingList<MsiFileItemView>(viewItems);
                    View.fileGrid.DataSource = fileDataSource;
                    View.AutoSizeFileGridColumns();
                    Status(fileDataSource.Count + " files found.");
                }
                catch (Exception eUnexpected)
                {
                    Error(string.Concat("Cannot view files:", eUnexpected.Message), eUnexpected);
                }
            }
        }
Example #17
0
        /// <summary>
        /// Run a Torch unit test.
        /// </summary>
        /// <param name="element">The unit test element.</param>
        /// <param name="previousUnitResults">The previous unit test results.</param>
        /// <param name="update">Indicates whether to give the user the option to fix a failing test.</param>
        /// <param name="args">The command arguments passed to WixUnit.</param>
        public static void RunUnitTest(XmlElement element, UnitResults previousUnitResults, bool update, ICommandArgs args)
        {
            string arguments = element.GetAttribute("Arguments");
            string expectedErrors = element.GetAttribute("ExpectedErrors");
            string expectedWarnings = element.GetAttribute("ExpectedWarnings");
            string extensions = element.GetAttribute("Extensions");
            string outputFile = element.GetAttribute("OutputFile");
            string targetDatabase = element.GetAttribute("TargetDatabase");
            string tempDirectory = element.GetAttribute("TempDirectory");
            string testName = element.ParentNode.Attributes["Name"].Value;
            string toolsDirectory = element.GetAttribute("ToolsDirectory");
            string updatedDatabase = element.GetAttribute("UpdatedDatabase");
            bool usePreviousOutput = ("true" == element.GetAttribute("UsePreviousOutput"));
            bool verifyTransform = ("true" == element.GetAttribute("VerifyTransform"));

            string toolFile = Path.Combine(toolsDirectory, "torch.exe");
            StringBuilder commandLine = new StringBuilder(arguments);

            // handle extensions
            if (!String.IsNullOrEmpty(extensions))
            {
                foreach (string extension in extensions.Split(';'))
                {
                    commandLine.AppendFormat(" -ext \"{0}\"", extension);
                }
            }

            // handle wixunit arguments
            if (args.NoTidy)
            {
                commandLine.Append(" -notidy");
            }

            // handle any previous outputs
            if (0 < previousUnitResults.OutputFiles.Count && usePreviousOutput)
            {
                commandLine.AppendFormat(" \"{0}\"", previousUnitResults.OutputFiles[0]);
                previousUnitResults.OutputFiles.Clear();
            }
            else // diff database files to create transform
            {
                commandLine.AppendFormat(" \"{0}\" \"{1}\"", targetDatabase, updatedDatabase);
            }

            if (null == outputFile || String.Empty == outputFile)
            {
                outputFile = Path.Combine(tempDirectory, "transform.mst");
            }
            commandLine.AppendFormat(" -out \"{0}\"", outputFile);
            previousUnitResults.OutputFiles.Add(outputFile);

            // run the tool
            ArrayList output = ToolUtility.RunTool(toolFile, commandLine.ToString());
            previousUnitResults.Errors.AddRange(ToolUtility.GetErrors(output, expectedErrors, expectedWarnings));
            previousUnitResults.Output.AddRange(output);

            // check the results
            if (verifyTransform && 0 == expectedErrors.Length && 0 == previousUnitResults.Errors.Count)
            {
                string actualDatabase = Path.Combine(tempDirectory, String.Concat(Guid.NewGuid(), ".msi"));
                File.Copy(targetDatabase, actualDatabase);
                File.SetAttributes(actualDatabase, File.GetAttributes(actualDatabase) & ~FileAttributes.ReadOnly);
                using (Database database = new Database(actualDatabase, OpenDatabase.Direct))
                {
                    // use transform validation bits set in the transform (if any; defaults to None).
                    database.ApplyTransform(outputFile);
                    database.Commit();
                }

                // check the output file
                ArrayList differences = CompareUnit.CompareResults(updatedDatabase, actualDatabase, testName, update);
                previousUnitResults.Errors.AddRange(differences);
                previousUnitResults.Output.AddRange(differences);
            }
        }
Example #18
0
File: Binder.cs Project: zooba/wix3
        /// <summary>
        /// Process uncompressed files.
        /// </summary>
        /// <param name="tempDatabaseFile">The temporary database file.</param>
        /// <param name="fileRows">The collection of files to copy into the image.</param>
        /// <param name="fileTransfers">Array of files to be transfered.</param>
        /// <param name="mediaRows">The indexed media rows.</param>
        /// <param name="layoutDirectory">The directory in which the image should be layed out.</param>
        /// <param name="compressed">Flag if source image should be compressed.</param>
        /// <param name="longNamesInImage">Flag if long names should be used.</param>
        private void ProcessUncompressedFiles(string tempDatabaseFile, FileRowCollection fileRows, ArrayList fileTransfers, MediaRowCollection mediaRows, string layoutDirectory, bool compressed, bool longNamesInImage)
        {
            if (0 == fileRows.Count || this.core.EncounteredError)
            {
                return;
            }

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

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

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

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

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

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

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

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

                            // finally put together the base media layout path and the relative file layout path
                            string fileLayoutPath = Path.Combine(mediaLayoutDirectory, relativeFileLayoutPath);
                            FileTransfer transfer;
                            if (FileTransfer.TryCreate(fileRow.Source, fileLayoutPath, false, "File", fileRow.SourceLineNumbers, out transfer))
                            {
                                fileTransfers.Add(transfer);
                            }
                        }
                    }
                }
            }
        }
Example #19
0
        /// <summary>
        /// Shows the table in the list on the view table tab.
        /// </summary>
        /// <param name="msidb">The msi database.</param>
        /// <param name="tableName">The name of the table.</param>
        private void UpdateMSiTableGrid(Database msidb, string tableName)
        {
            if (msidb == null || string.IsNullOrEmpty(tableName))
                return;

            Status(string.Concat("Processing Table \'", tableName, "\'."));

            using (new DisposableCursor(View))
            {   // clear the columns no matter what happens (in the event the table doesn't exist we don't want to show anything).
                View.ClearTableViewGridColumns();
                try
                {
                    // NOTE: Deliberately not calling msidb.TableExists here as some System tables could not be read due to using it.
                    string query = string.Concat("SELECT * FROM `", tableName, "`");

                    using (var view = new ViewWrapper(msidb.OpenExecuteView(query)))
                    {
                        foreach (ColumnInfo col in view.Columns)
                        {
                            View.AddTableViewGridColumn(string.Concat(col.Name, " (", col.TypeID, ")"));
                        }
                        View.SetTableViewGridDataSource(view.Records);
                    }
                    Status("Idle");
                }
                catch (Exception eUnexpected)
                {
                    Error(string.Concat("Cannot view table:", eUnexpected.Message), eUnexpected);
                }
            }
        }
Example #20
0
File: Binder.cs Project: zooba/wix3
        /// <summary>
        /// Sets the codepage of a database.
        /// </summary>
        /// <param name="db">Database to set codepage into.</param>
        /// <param name="output">Output with the codepage for the database.</param>
        private void SetDatabaseCodepage(Database db, Output output)
        {
            // write out the _ForceCodepage IDT file
            string idtPath = Path.Combine(this.TempFilesLocation, "_ForceCodepage.idt");
            using (StreamWriter idtFile = new StreamWriter(idtPath, false, Encoding.ASCII))
            {
                idtFile.WriteLine(); // dummy column name record
                idtFile.WriteLine(); // dummy column definition record
                idtFile.Write(output.Codepage);
                idtFile.WriteLine("\t_ForceCodepage");
            }

            // try to import the table into the MSI
            try
            {
                db.Import(Path.GetDirectoryName(idtPath), Path.GetFileName(idtPath));
            }
            catch (WixInvalidIdtException)
            {
                // the IDT should be valid, so an invalid code page was given
                throw new WixException(WixErrors.IllegalCodepage(output.Codepage));
            }
        }
Example #21
0
        /// <summary>
        /// Instantiate a new SummaryInformation class from an open database.
        /// </summary>
        /// <param name="db">Database to retrieve summary information from.</param>
        public SummaryInformation(Database db)
        {
            if (null == db)
            {
                throw new ArgumentNullException("db");
            }

            uint handle = 0;
            int error = MsiInterop.MsiGetSummaryInformation(db.Handle, null, 0, ref handle);
            if (0 != error)
            {
                throw new MsiException(error);
            }
            this.Handle = handle;
        }
Example #22
0
        public void LoadTables()
        {
            var allTableNames = new string[]
            {
                #region Hard Coded Table Names
                //FYI: This list is from http://msdn.microsoft.com/en-us/library/2k3te2cs%28VS.100%29.aspx
                "ActionText",
                "AdminExecuteSequence ",
                "AdminUISequence",
                "AdvtExecuteSequence",
                "AdvtUISequence",
                "AppId",
                "AppSearch",
                "BBControl",
                "Billboard",
                "Binary",
                "BindImage",
                "CCPSearch",
                "CheckBox",
                "Class",
                "ComboBox",
                "CompLocator",
                "Complus",
                "Component",
                "Condition",
                "Control",
                "ControlCondition",
                "ControlEvent",
                "CreateFolder",
                "CustomAction",
                "Dialog",
                "Directory",
                "DrLocator",
                "DuplicateFile",
                "Environment",
                "Error",
                "EventMapping",
                "Extension",
                "Feature",
                "FeatureComponents",
                "File",
                "FileSFPCatalog",
                "Font",
                "Icon",
                "IniFile",
                "IniLocator",
                "InstallExecuteSequence",
                "InstallUISequence",
                "IsolatedComponent",
                "LaunchCondition",
                "ListBox",
                "ListView",
                "LockPermissions",
                "Media",
                "MIME",
                "MoveFile",
                "MsiAssembly",
                "MsiAssemblyName",
                "MsiDigitalCertificate",
                "MsiDigitalSignature",
                "MsiEmbeddedChainer",
                "MsiEmbeddedUI",
                "MsiFileHash",
                "MsiLockPermissionsEx Table",
                "MsiPackageCertificate",
                "MsiPatchCertificate",
                "MsiPatchHeaders",
                "MsiPatchMetadata",
                "MsiPatchOldAssemblyName",
                "MsiPatchOldAssemblyFile",
                "MsiPatchSequence",
                "MsiServiceConfig",
                "MsiServiceConfigFailureActions",
                "MsiSFCBypass",
                "ODBCAttribute",
                "ODBCDataSource",
                "ODBCDriver",
                "ODBCSourceAttribute",
                "ODBCTranslator",
                "Patch",
                "PatchPackage",
                "ProgId",
                "Property",
                "PublishComponent",
                "RadioButton",
                "Registry",
                "RegLocator",
                "RemoveFile",
                "RemoveIniFile",
                "RemoveRegistry",
                "ReserveCost",
                "SelfReg",
                "ServiceControl",
                "ServiceInstall",
                "SFPCatalog",
                "Shortcut",
                "Signature",
                "TextStyle",
                "TypeLib",
                "UIText",
                "Verb",
                "_Validation",
                "_Columns",
                "_Streams",
                "_Storages",
                "_Tables",
                "_TransformView Table",
                "Upgrade"
                #endregion
            };

            var systemTables = new string[]
            {
                "_Validation",
                "_Columns",
                "_Streams",
                "_Storages",
                "_Tables",
                "_TransformView Table"
            };

            IEnumerable<string> msiTableNames = allTableNames;

            using (var msidb = new Database(View.SelectedMsiFile.FullName, OpenDatabase.ReadOnly))
            {
                using (new DisposableCursor(View))
                {
                    try
                    {
                        Status("Loading list of tables...");
                        var query = "SELECT * FROM `_Tables`";
                        using (var msiTable = new ViewWrapper(msidb.OpenExecuteView(query)))
                        {
                            var tableNames = from record in msiTable.Records
                                select record[0] as string;
                            //NOTE: system tables are not usually in the _Tables table.
                            var tempList = tableNames.ToList();
                            tempList.AddRange(systemTables);
                            msiTableNames = tempList.ToArray();
                        }

                        Status("");
                    }
                    catch (Exception e)
                    {
                        Status(e.Message);
                    }

                    View.cboTable.Items.Clear();
                    View.cboTable.Items.AddRange(msiTableNames.ToArray());
                    View.cboTable.SelectedIndex = 0;
                }
            }
        }
Example #23
0
File: Binder.cs Project: zooba/wix3
        /// <summary>
        /// Creates the MSI/MSM/PCP database.
        /// </summary>
        /// <param name="output">Output to create database for.</param>
        /// <param name="databaseFile">The database file to create.</param>
        /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param>
        /// <param name="useSubdirectory">Whether to use a subdirectory based on the <paramref name="databaseFile"/> file name for intermediate files.</param>
        internal void GenerateDatabase(Output output, string databaseFile, bool keepAddedColumns, bool useSubdirectory)
        {
            // add the _Validation rows
            if (!this.suppressAddingValidationRows)
            {
                Table validationTable = output.EnsureTable(this.core.TableDefinitions["_Validation"]);

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

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

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

            try
            {
                OpenDatabase type = OpenDatabase.CreateDirect;

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

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

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

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

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

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

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

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

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

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

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

                                // Otherwise we rethrow the InvalidIdt
                                throw;
                            }
                        }

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

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

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

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

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

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

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

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

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

                    // we're good, commit the changes to the new MSI
                    db.Commit();
                }
            }
            catch (IOException)
            {
                // TODO: this error message doesn't seem specific enough
                throw new WixFileNotFoundException(SourceLineNumberCollection.FromFileName(databaseFile), databaseFile);
            }
        }
Example #24
0
        /// <summary>
        /// Opens the previous package (.msi or .exe) and reads the interesting information from it.
        /// </summary>
        /// <param name="filePath">Path to the package.</param>
        private void ReadPreviousPackage(string filePath)
        {
            using (Database db = new Database(filePath, OpenDatabase.ReadOnly))
            {
                using (View view = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`=?"))
                {

                    string propertyValue;

                    // get the UpgradeCode
                    propertyValue = this.FetchPropertyValue(view, "UpgradeCode");
                    if (propertyValue != null)
                    {
                        this.previousUpgradeCode = new Guid(propertyValue);
                    }

                    // get the Version
                    propertyValue = this.FetchPropertyValue(view, "ProductVersion");
                    if (propertyValue != null)
                    {
                        this.previousVersion = new Version(propertyValue);
                    }

                    // get the Update URL
                    propertyValue = this.FetchPropertyValue(view, "ARPURLUPDATEINFO");
                    if (propertyValue != null)
                    {
                        this.previousUri = new Uri(propertyValue);
                    }
                }
            }
        }
Example #25
0
 /// <summary>
 /// Shows the table based on the current UI selections in the view (selected MSI and selected table).
 /// </summary>
 public void UpdateMSiTableGrid()
 {
     using (var msidb = new Database(View.SelectedMsiFile.FullName, OpenDatabase.ReadOnly))
     {
         string tableName = View.SelectedTableName;
         UpdateMSiTableGrid(msidb, tableName);
     }
 }
Example #26
0
 /// <summary>
 /// Merges two databases together.
 /// </summary>
 /// <param name="mergeDatabase">The database to merge into the base database.</param>
 /// <param name="tableName">The name of the table to receive merge conflict information.</param>
 public void Merge(Database mergeDatabase, string tableName)
 {
     int error = MsiInterop.MsiDatabaseMerge(this.Handle, mergeDatabase.Handle, tableName);
     if (0 != error)
     {
         throw new MsiException(error);
     }
 }
Example #27
0
        /// <summary>
        /// Creates a transform that, when applied to the reference database, results in this database.
        /// </summary>
        /// <param name="referenceDatabase">Required database that does not include the changes.</param>
        /// <param name="transformFile">The name of the generated transform file. This is optional.</param>
        /// <returns>true if a transform is generated; false if a transform is not generated because
        /// there are no differences between the two databases.</returns>
        public bool GenerateTransform(Database referenceDatabase, string transformFile)
        {
            int error = MsiInterop.MsiDatabaseGenerateTransform(this.Handle, referenceDatabase.Handle, transformFile, 0, 0);
            if (0 != error && 0xE8 != error) // ERROR_NO_DATA(0xE8) means no differences were found
            {
                throw new MsiException(error);
            }

            return (0xE8 != error);
        }
Example #28
0
 /// <summary>
 /// Updates the MSI property tab/list
 /// </summary>
 public void UpdatePropertyTabView()
 {
     try
     {
         MsiPropertyInfo[] props;
         using (var msidb = new Database(View.SelectedMsiFile.FullName, OpenDatabase.ReadOnly))
         {
             props = MsiPropertyInfo.GetPropertiesFromDatabase(msidb);
         }
         View.SetPropertyGridDataSource(props);
     }
     catch (Exception eUnexpected)
     {
         Error("Error loading Summary Information", eUnexpected);
     }
 }
Example #29
0
        /// <summary>
        /// Constructor that creates a view given a database handle and a query.
        /// </summary>
        /// <param name="db">Handle to the database to run the query on.</param>
        /// <param name="query">Query to be executed.</param>
        public View(Database db, string query)
        {
            if (IntPtr.Zero != this.handle)
            {
                throw new ArgumentNullException();   // TODO: come up with a real exception to throw
            }

            uint error = MsiInterop.MsiDatabaseOpenView(db.InternalHandle, query, out this.handle);
            if (0 != error)
            {
                throw new System.Runtime.InteropServices.ExternalException(String.Concat("Failed to create view with query: ", query), (int)error);
            }
        }
Example #30
0
 /// <summary>
 /// Updates the ui with the currently selected msi file.
 /// </summary>
 public void ViewFiles()
 {
     using (var msidb = new Database(View.SelectedMsiFile.FullName, OpenDatabase.ReadOnly))
     {
         ViewFiles(msidb);
         ToggleSelectAllFiles(true);
     }
 }