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()); }
/// <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))); }
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()); }
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); }
/// <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); } }
/// <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); } } }
public MsiFileItemView(MsiFile msiDataFile) { _file = msiDataFile; }
/// <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))); }
/// <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)); }
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; }