// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. using System; using System.IO; using System.Reflection; using System.Collections; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text.RegularExpressions; using WixToolset.Dtf.WindowsInstaller; using WixToolset.Dtf.WindowsInstaller.Package; [assembly: AssemblyDescription("Windows Installer package file extraction and update tool")] /// /// Shows sample use of the InstallPackage class. /// public class WiFile { public static void Usage(TextWriter w) { w.WriteLine("Usage: WiFile.exe package.msi /l [filename,filename2,...]"); w.WriteLine("Usage: WiFile.exe package.msi /x [filename,filename2,...]"); w.WriteLine("Usage: WiFile.exe package.msi /u [filename,filename2,...]"); w.WriteLine(); w.WriteLine("Lists (/l), extracts (/x) or updates (/u) files in an MSI or MSM."); w.WriteLine("Files are extracted using their source path relative to the package."); w.WriteLine("Specified filenames do not include paths."); w.WriteLine("Filenames may be a pattern such as *.exe or file?.dll"); } [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] public static int Main(string[] args) { if(!(args.Length == 2 || args.Length == 3)) { Usage(Console.Out); return -1; } string msiFile = args[0]; string option = args[1].ToLowerInvariant(); if(option.StartsWith("-", StringComparison.Ordinal)) option = "/" + option.Substring(1); string[] fileNames = null; if(args.Length == 3) { fileNames = args[2].Split(','); } try { switch(option) { case "/l": using(InstallPackage pkg = new InstallPackage(msiFile, DatabaseOpenMode.ReadOnly)) { pkg.Message += new InstallPackageMessageHandler(Console.WriteLine); IEnumerable fileKeys = (fileNames != null ? FindFileKeys(pkg, fileNames) : pkg.Files.Keys); foreach(string fileKey in fileKeys) { Console.WriteLine(pkg.Files[fileKey]); } } break; case "/x": using(InstallPackage pkg = new InstallPackage(msiFile, DatabaseOpenMode.ReadOnly)) { pkg.Message += new InstallPackageMessageHandler(Console.WriteLine); ICollection fileKeys = FindFileKeys(pkg, fileNames); pkg.ExtractFiles(fileKeys); } break; case "/u": using(InstallPackage pkg = new InstallPackage(msiFile, DatabaseOpenMode.Transact)) { pkg.Message += new InstallPackageMessageHandler(Console.WriteLine); ICollection fileKeys = FindFileKeys(pkg, fileNames); pkg.UpdateFiles(fileKeys); pkg.Commit(); } break; default: Usage(Console.Out); return -1; } } catch(InstallerException iex) { Console.WriteLine("Error: " + iex.Message); return iex.ErrorCode != 0 ? iex.ErrorCode : 1; } catch(FileNotFoundException fnfex) { Console.WriteLine(fnfex.Message); return 2; } catch(Exception ex) { Console.WriteLine("Error: " + ex.Message); return 1; } return 0; } static ICollection FindFileKeys(InstallPackage pkg, ICollection fileNames) { List fileKeys = null; if(fileNames != null) { fileKeys = new List(); foreach(string fileName in fileNames) { string[] foundFileKeys = null; if(fileName.IndexOfAny(new char[] { '*', '?' }) >= 0) { foundFileKeys = pkg.FindFiles(FilePatternToRegex(fileName)); } else { foundFileKeys = pkg.FindFiles(fileName); } fileKeys.AddRange(foundFileKeys); } if(fileKeys.Count == 0) { throw new FileNotFoundException("Files not found in package."); } } return fileKeys; } static Regex FilePatternToRegex(string pattern) { return new Regex("^" + Regex.Escape(pattern).Replace("\\*", ".*").Replace("\\?", ".") + "$", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); } }