예제 #1
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);
            }
        }
예제 #2
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>
        /// <param name="previousUpgradeCode">Upgrade code of the package.</param>
        /// <param name="previousVersion">Version of the package.</param>
        /// <param name="previousUri">Update URL of the package.</param>
        private void ReadPreviousPackage(string filePath, out Guid previousUpgradeCode, out Version previousVersion, out Uri previousUri)
        {
            // assume nothing about the previous package
            previousUpgradeCode = Guid.Empty;
            previousVersion = null;
            previousUri = null;

            string tempFileName = null;
            Database db = null;
            View view = null;

            try
            {
                string msiPath = filePath; // assume the file path is the path to the MSI

                // if the extension on the file path is ".exe" try to extract the MSI out of it
                if (String.Compare(Path.GetExtension(filePath), ".exe", true) == 0)
                {
                    tempFileName = Path.GetTempFileName();

                    Process process = new Process();
                    process.StartInfo.FileName = filePath;
                    process.StartInfo.CreateNoWindow = true;
                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.Arguments = String.Concat("-out ", tempFileName);

                    process.Start();
                    process.WaitForExit();

                    if (process.ExitCode != 0)
                    {
                        throw new ApplicationException(String.Concat("Failed to extract MSI from ", process.StartInfo.FileName));
                    }

                    msiPath = tempFileName; // the MSI is now at the temp filename location
                }

                db = new Database(msiPath, OpenDatabase.ReadOnly);
                view = db.OpenView("SELECT `Value` FROM `Property` WHERE `Property`=?");

                string propertyValue;

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

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

                // get the Update URL
                propertyValue = this.FetchPropertyValue(view, "ARPURLUPDATEINFO");
                if (propertyValue != null)
                {
                    previousUri = new Uri(propertyValue);
                }
            }
            finally
            {
                if (view != null)
                {
                    view.Close();
                }

                if (db != null)
                {
                    db.Close();
                }

                if (tempFileName != null)
                {
                    File.Delete(tempFileName);
                }
            }
        }