Example #1
0
        private static MsiFile[] GetMsiFileFromFileNames(FileInfo msi, IEnumerable <string> fileNamesToExtract)
        {
            if (msi == null)
            {
                throw new ArgumentNullException("msi");
            }
            var msiFiles = MsiFile.CreateMsiFilesFromMsi(msi.FullName);

            Array.Sort(msiFiles, (f1, f2) => string.Compare(f1.LongFileName, f2.LongFileName, StringComparison.InvariantCulture));

            var fileNamesToExtractAsMsiFiles = new List <MsiFile>();

            foreach (var fileName in fileNamesToExtract)
            {
                var found = Array.BinarySearch(msiFiles, fileName, FileNameComparer.Default);
                if (found >= 0)
                {
                    fileNamesToExtractAsMsiFiles.Add(msiFiles[found]);
                }
                else
                {
                    Console.WriteLine("File {0} was not found in the msi.", fileName);
                }
            }
            return(fileNamesToExtractAsMsiFiles.ToArray());
        }
Example #2
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 #3
0
        private static MsiFile[] GetMsiFileFromFileNames(Path msi, string[] fileNamesToExtract)
        {
            var msiFiles = MsiFile.CreateMsiFilesFromMSI(msi);

            Array.Sort(msiFiles, (f1, f2) => string.Compare(f1.LongFileName, f2.LongFileName, StringComparison.InvariantCulture));

            var fileNamesToExtractAsMsiFiles = new List <MsiFile>();

            foreach (var fileName in fileNamesToExtract)
            {
                var found = Array.BinarySearch(msiFiles, fileName, FileNameComparer.Default);
                if (found >= 0)
                {
                    fileNamesToExtractAsMsiFiles.Add(msiFiles[found]);
                }
                else
                {
                    Console.WriteLine("File {0} was not found in the msi.", fileName);
                }
            }
            return(fileNamesToExtractAsMsiFiles.ToArray());
        }
Example #4
0
        private static MsiDirectory GetDirectoryForFile(MsiFile file, MsiDirectory[] allDirectories, IDictionary componentsByComponentTable)
        {
            // get the component for the file
            var componentRow = componentsByComponentTable[file._component] as TableRow;

            if (componentRow != null)
            {
                var componentDirectory = componentRow.GetString("Directory_");
                var 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);
            }
            // found component, get the directory:
            Debug.Assert(false, "File '{0}' has no component entry.", file.LongFileName);
            return(null);
        }
Example #5
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 #6
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(Path msi, string outputDir, MsiFile[] filesToExtract, AsyncCallback progressCallback)
        {
            if (msi.IsEmpty)
            {
                throw new ArgumentNullException("msi");
            }
            if (string.IsNullOrEmpty(outputDir))
            {
                throw new ArgumentNullException("outputDir");
            }

            int filesExtractedSoFar = 0;
            //Refrence on Embedding files: https://msdn.microsoft.com/en-us/library/aa369279.aspx
            ExtractionProgress progress = null;
            Database           msidb    = new Database(msi.PathString, OpenDatabase.ReadOnly);

            try
            {
                if (filesToExtract == null || filesToExtract.Length < 1)
                {
                    filesToExtract = MsiFile.CreateMsiFilesFromMSI(msidb);
                }

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

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

                progress.ReportProgress(ExtractionActivity.Initializing, "", filesExtractedSoFar);
                FileSystem.CreateDirectory(new Path(outputDir));

                //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(msi, 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);
                            string      targetDirectoryForFile = GetTargetDirectory(outputDir, entry.Directory);
                            LessIO.Path destName = LessIO.Path.Combine(targetDirectoryForFile, entry.LongFileName);
                            if (FileSystem.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;
                                Path uniqueName;
                                do
                                {
                                    uniqueName = new Path(destName + "." + "duplicate" + ++duplicateCount);
                                } while (FileSystem.Exists(uniqueName));
                                destName = uniqueName;
                                // ReSharper restore HeuristicUnreachableCode
                            }
                            Trace.WriteLine(string.Concat("Extracting File \'", compressedFile.Filename, "\' to \'", destName, "\'"));
                            compressedFile.ExtractTo(destName.PathString);
                            filesExtractedSoFar++;
                        }
                    }
                }
                finally
                {               //cleanup the decompressors allocated in MergeCabs
                    foreach (var decomp in cabDecompressors)
                    {
                        decomp.Close(false);
                        DeleteFileForcefully(new Path(decomp.LocalFilePath));
                    }
                }
            }
            finally
            {
                if (msidb != null)
                {
                    msidb.Close();
                }
                if (progress != null)
                {
                    progress.ReportProgress(ExtractionActivity.Complete, "", filesExtractedSoFar);
                }
            }
        }
Example #7
0
 public MsiFileItemView(MsiFile msiDataFile)
 {
     _file = msiDataFile;
 }
Example #8
0
 /// <summary>
 /// Creates a list of <see cref="MsiFile"/> objects from the specified database.
 /// </summary>
 public static MsiFile[] GetMsiFilesFromMSI(string msiDatabaseFilePath)
 {
     return(MsiFile.CreateMsiFilesFromMSI(new Path(msiDatabaseFilePath)));
 }
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
        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;
			
        }