Example #1
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 #2
0
        private static MsiDirectory GetDirectoryForFile(MsiFile file, MsiDirectory[] allDirectories, IDictionary componentsByComponentTable)
        {
            // get the component for the file
            TableRow componentRow = componentsByComponentTable[file.Component] as TableRow;

            if (componentRow == null)
            {
                Debug.Assert(false, "File '{0}' has no component entry.", file.LongFileName);
                return(null);
            }
            // found component, get the directory:
            string       componentDirectory = componentRow.GetString("Directory_");
            MsiDirectory directory          = FindDirectoryByDirectoryKey(allDirectories, componentDirectory);

            if (directory != null)
            {
                //Trace.WriteLine(string.Format("Directory for '{0}' is '{1}'.", file.LongFileName, directory.GetPath()));
            }
            else
            {
                Debug.Fail(string.Format("directory not found for file '{0}'.", file.LongFileName));
            }
            return(directory);
        }
Example #3
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);
            }

            // Adding directory to the table if it is specified as parent, but missing in the directories database.
            // This is workaround in case directories database is modify in runtime.
            var parentsList = directoriesByDirID.Values.OfType <MsiDirectory>().Select(v => v.DirectoryParent).Distinct().ToList();
            foreach (var parent in parentsList)
            {
                if (!string.IsNullOrEmpty(parent) && !directoriesByDirID.ContainsKey(parent))
                {
                    MsiDirectory directory = new MsiDirectory()
                    {
                        _directoryParent = "",
                        _directory       = parent,
                        _targetName      = 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 the value of the Directory_Parent column is null...
                var isRoot = string.IsNullOrEmpty(dir.DirectoryParent);
                //  ...or is equal to the Directory column, the DefaultDir column specifies the name of a root source directory. - https://msdn.microsoft.com/en-us/library/windows/desktop/aa368295(v=vs.85).aspx
                if (!isRoot && string.Equals(dir.Directory, dir.DirectoryParent, StringComparison.InvariantCulture))
                {
                    isRoot = true;
                }
                if (isRoot)
                {
                    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;
        }