public void NoTransformViewChanges() { var package = Path.Combine(base.TestContext.DeploymentDirectory, "Example.msi"); using (var db = new InstallPackage(package, DatabaseOpenMode.ReadOnly)) { var view = new TransformView(db); Assert.AreEqual<int>(0, view.Tables.Count); } }
/// <summary> /// Creates a new instance of the <see cref="PatchApplicator"/> class. /// </summary> /// <param name="db">The <see cref="InstallPackage"/> to transform.</param> internal PatchApplicator(InstallPackage db) { if (null == db) { throw new ArgumentNullException("db"); } this.db = db; this.sequencer = new PatchSequencer(); }
public MsiManager(string msiFile) { _msiFile = msiFile; var ispackage = Installer.VerifyPackage(msiFile); if (!ispackage) { throw new ArgumentException("Not a valid MSI file", msiFile); } _installPackage = new InstallPackage(msiFile, DatabaseOpenMode.ReadOnly); _productCode = _installPackage.Property["ProductCode"]; }
/// <summary> /// Opens the package read-only and sets the <see cref="InstallCommandActionData.ProductCode"/> property. /// </summary> public void SetProductCode() { using (var db = new InstallPackage(this.Path, DatabaseOpenMode.ReadOnly)) { if (db.Tables.Contains("Property")) { this.ProductCode = db.Property["ProductCode"]; } else { this.ProductCode = null; } } }
/// <summary> /// Applies any applicable transforms from <see cref="Patch"/> and <see cref="Transform"/> to the given package. /// </summary> /// <param name="db">The <see cref="InstallPackage"/> database to which applicable transforms are applied.</param> protected void ApplyTransforms(InstallPackage db) { // Apply transforms first since they likely apply to the unpatched product. if (0 < this.Transform.Count()) { this.Transform = this.ResolveFiles(this.Transform).ToArray(); foreach (string path in this.Transform) { try { db.ApplyTransform(path, PatchApplicator.IgnoreErrors); db.ApplyTransform(path, PatchApplicator.IgnoreErrors | TransformErrors.ViewTransform); } catch (InstallerException ex) { using (var pse = new PSInstallerException(ex)) { if (null != pse.ErrorRecord) { base.WriteError(pse.ErrorRecord); } } } } db.Commit(); } // Apply applicable patch transforms. if (0 < this.Patch.Count()) { this.Patch = this.ResolveFiles(this.Patch).ToArray(); var applicator = new PatchApplicator(db); foreach (string path in this.Patch) { applicator.Add(path); } applicator.InapplicablePatch += (source, args) => { var message = string.Format(CultureInfo.CurrentCulture, Resources.Error_InapplicablePatch, args.Patch, args.Product); base.WriteVerbose(message); }; // The applicator will commit the changes. applicator.Apply(); } }
private static void ExtractFilesInBinaryTable(InstallPackage installPackage, ICollection<string> names, string tableName, string path) { if (!Directory.Exists(path)) { throw new ArgumentException(string.Format("The path specified does not exist. {0}", path), "path"); } View binaryView = installPackage.OpenView("Select `Name`, `Data` FROM `{0}`", tableName); binaryView.Execute(); ICollection<string> createdFiles = new List<string>(100); for (Record binaryRec = binaryView.Fetch(); binaryRec != null; binaryRec = binaryView.Fetch()) { string binaryKey = (string)binaryRec[1]; Stream binaryData = (Stream)binaryRec[2]; if (null != names && !names.Contains(binaryKey)) continue; //Skip unspecified values createdFiles.Add(binaryKey); FileInfo binaryFile = new FileInfo(Path.Combine(path, binaryKey)); using (FileStream fs = binaryFile.Create()) { Stream tempBuffer = new MemoryStream((int)binaryFile.Length); for (int a = binaryData.ReadByte(); a != -1; a = binaryData.ReadByte()) { tempBuffer.WriteByte((byte)a); } tempBuffer.Seek(0, SeekOrigin.Begin); for (int a = tempBuffer.ReadByte(); a != -1; a = tempBuffer.ReadByte()) { fs.WriteByte((byte)a); } } } InstallPackage.ClearReadOnlyAttribute(path, createdFiles); }
public void TransfomViewChanges() { var package = Path.Combine(base.TestContext.DeploymentDirectory, "Example.msi"); var patch = Path.Combine(base.TestContext.DeploymentDirectory, "Example.msp"); using (var db = new InstallPackage(package, DatabaseOpenMode.ReadOnly)) { var applicator = new PatchApplicator(db); applicator.Add(patch); applicator.Apply(true); var view = new TransformView(db); // Despite Orca showing 5 tables, the _TransformView table does not show the created, empty "Patch" table. Assert.AreEqual<int>(4, view.Tables.Count); Assert.AreEqual<TableOperation>(TableOperation.Modify, view.GetTableOperation("Media")); Assert.AreEqual<RowOperation>(RowOperation.None, view.GetRowOperation("Media", "1")); Assert.AreEqual<RowOperation>(RowOperation.Insert, view.GetRowOperation("Media", "100")); Assert.AreEqual<TableOperation>(TableOperation.Create, view.GetTableOperation("PatchPackage")); Assert.AreEqual<RowOperation>(RowOperation.Insert, view.GetRowOperation("PatchPackage", "{FF63D787-26E2-49CA-8FAA-28B5106ABD3A}")); Assert.AreEqual<TableOperation>(TableOperation.Modify, view.GetTableOperation("Property")); Assert.AreEqual<RowOperation>(RowOperation.None, view.GetRowOperation("Property", "ProductCode")); Assert.AreEqual<RowOperation>(RowOperation.Modify, view.GetRowOperation("Property", "ProductVersion")); Assert.AreEqual<RowOperation>(RowOperation.Insert, view.GetRowOperation("Property", "Example.PatchCode")); Assert.AreEqual<RowOperation>(RowOperation.Insert, view.GetRowOperation("Property", "Example.AllowRemoval")); Assert.AreEqual<TableOperation>(TableOperation.Modify, view.GetTableOperation("Registry")); Assert.AreEqual<RowOperation>(RowOperation.Modify, view.GetRowOperation("Registry", "reg302A797C45AD3AD1EC816DDC58DF65F3")); // Negative assertions. Assert.AreEqual<TableOperation>(TableOperation.None, view.GetTableOperation("File")); Assert.AreEqual<RowOperation>(RowOperation.None, view.GetRowOperation(null, null)); Assert.AreEqual<RowOperation>(RowOperation.None, view.GetRowOperation("File", null)); Assert.AreEqual<RowOperation>(RowOperation.None, view.GetRowOperation("File", "product.wxs")); } }
internal InstallPackageProperties(InstallPackage installPackage) { this.installPackage = installPackage; }
private static InstallPackage Copy(InstallPackage db) { var temp = Path.ChangeExtension(Path.GetTempFileName(), ".msi"); File.Copy(db.FilePath, temp, true); // Open a copy and schedule delete it when closed. var copy = new InstallPackage(temp, DatabaseOpenMode.ReadOnly); copy.DeleteOnClose(temp); return copy; }
/// <summary> /// Analyzes the transforms included in the patch package to find the ones that /// are applicable to an install package. /// </summary> /// <param name="installPackage">The install package to validate the transforms against</param> /// <returns>Array of valid transform names</returns> /// <remarks> /// The returned list does not include the "patch special transforms" that /// are prefixed with "#" If a transform is valid, then its corresponding /// special transform is assumed to be valid as well. /// </remarks> public string[] GetValidTransforms(InstallPackage installPackage) { ArrayList transformArray = new ArrayList(); string transformList = this.SummaryInfo.LastSavedBy; foreach(string transform in transformList.Split(';', ':')) { if(transform.Length != 0 && !transform.StartsWith("#", StringComparison.Ordinal)) { this.LogMessage("Checking validity of transform {0}", transform); string tempTransformFile = null; try { tempTransformFile = Path.GetTempFileName(); this.ExtractTransform(transform, tempTransformFile); if(installPackage.IsTransformValid(tempTransformFile)) { this.LogMessage("Found valid transform: {0}", transform); transformArray.Add(transform); } } finally { if(tempTransformFile != null && File.Exists(tempTransformFile)) { try { File.Delete(tempTransformFile); } catch(IOException) { } } } } } return (string[]) transformArray.ToArray(typeof(string)); }
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<string> 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<string> 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<string> 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; }
/// <summary> /// Extracts files from an MSI database and rewrites the paths embedded in the source .wixpdb to the output .wixpdb. /// </summary> private void MeltProduct() { // print friendly message saying what file is being decompiled Console.WriteLine("{0} / {1}", Path.GetFileName(this.inputFile), Path.GetFileName(this.inputPdbFile)); // extract files from the .msi (unless suppressed) and get the path map of File ids to target paths string outputDirectory = this.exportBasePath ?? Environment.GetEnvironmentVariable("WIX_TEMP"); IDictionary<string, string> paths = null; using (InstallPackage package = new InstallPackage(this.inputFile, DatabaseOpenMode.ReadOnly, null, outputDirectory)) { if (!this.suppressExtraction) { package.ExtractFiles(); } paths = package.Files.SourcePaths; } Pdb inputPdb = Pdb.Load(this.inputPdbFile, true, true); Table wixFileTable = inputPdb.Output.Tables["WixFile"]; if (null != wixFileTable) { foreach (Row row in wixFileTable.Rows) { WixFileRow fileRow = row as WixFileRow; if (null != fileRow) { string newPath; if (paths.TryGetValue(fileRow.File, out newPath)) { fileRow.Source = Path.Combine(outputDirectory, newPath); } } } } string tempPath = Path.Combine(Environment.GetEnvironmentVariable("WIX_TEMP") ?? Path.GetTempPath(), Path.GetRandomFileName()); try { inputPdb.Save(this.outputFile, null, null, tempPath); } finally { if (this.tidy) { if (!AppCommon.DeleteDirectory(tempPath, this.messageHandler)) { Console.WriteLine(MeltStrings.WAR_FailedToDeleteTempDir, tempPath); } } else { Console.WriteLine(MeltStrings.INF_TempDirLocatedAt, tempPath); } } }
/// <summary> /// Extracts files from an MSI database and rewrites the paths embedded in the source .wixpdb to the output .wixpdb. /// </summary> private void MeltProduct() { // print friendly message saying what file is being decompiled Console.WriteLine(Path.GetFileName(this.inputFile), "/", Path.GetFileName(this.inputPdbFile)); // extract files from the .msi and get the path map of File ids to target paths string outputDirectory = this.exportBasePath ?? Environment.GetEnvironmentVariable("WIX_TEMP"); IDictionary<string, string> paths = null; using (InstallPackage package = new InstallPackage(this.inputFile, DatabaseOpenMode.ReadOnly, null, outputDirectory)) { package.ExtractFiles(); paths = package.Files.SourcePaths; } Pdb inputPdb = Pdb.Load(this.inputPdbFile, true, true); if (null != inputPdb) { Table wixFileTable = inputPdb.Output.Tables["WixFile"]; if (null != wixFileTable) { foreach (Row row in wixFileTable.Rows) { WixFileRow fileRow = row as WixFileRow; if (null != fileRow) { string newPath; if (paths.TryGetValue(fileRow.File, out newPath)) { fileRow.Source = Path.Combine(outputDirectory, newPath); } } } } inputPdb.Save(this.outputFile, null, null, outputDirectory); } }
/// <summary> /// Checks to make sure that the debug symbols match up with the MSI. /// This is to help in ensuring that error 1642 does not inexplicably appear. /// </summary> /// <remarks> /// This is meant to assist with Bug # 4792 /// http://wixtoolset.org/issues/4792/ /// </remarks> /// <param name="package"> /// The MSI currently being melted. /// </param> /// <param name="inputPdb"> /// The debug symbols package being compared against the <paramref name="package"/>. /// </param> /// <returns></returns> private static bool ValidateMsiMatchesPdb(InstallPackage package, Pdb inputPdb) { string msiPackageCode = (string)package.Property["PackageCode"]; foreach (Row pdbPropertyRow in inputPdb.Output.Tables["Property"].Rows) { if ("PackageCode" == (string)pdbPropertyRow.Fields[0].Data) { string pdbPackageCode = (string)pdbPropertyRow.Fields[1].Data; if (msiPackageCode != pdbPackageCode) { Console.WriteLine(MeltStrings.WAR_MismatchedPackageCode, msiPackageCode, pdbPackageCode); return false; } break; } } return true; }
/// <summary> /// Extract binary data from tables with a Name and Data column in them. /// </summary> /// <param name="inputPdb">A reference to a <see cref="Pdb"/> as output. Paths (Data properties) will be modified in this object.</param> /// <param name="package">The installer database to rip from.</param> /// <param name="exportPath">The full path where files will be exported to.</param> /// <param name="tableName">The name of the table to export.</param> private static void MeltBinaryTable(Pdb inputPdb, InstallPackage package, string exportPath, string tableName) { if (string.IsNullOrEmpty(tableName)) { throw new ArgumentNullException("tableName"); } if (string.IsNullOrEmpty(exportPath)) { throw new ArgumentNullException("exportPath"); } if (null == package) { throw new ArgumentNullException("package"); } if (null == inputPdb) { throw new ArgumentNullException("inputPdb"); } Table pdbTable = inputPdb.Output.Tables[tableName]; if (null == pdbTable) { Console.WriteLine("Table {0} does not exist.", tableName); return; } try { Directory.CreateDirectory(exportPath); Melt.ExtractFilesInBinaryTable(package, null, tableName, exportPath); IDictionary<string, string> paths = package.GetFilePaths(exportPath); if (null != paths) { foreach (Row row in pdbTable.Rows) { string filename = (string)row.Fields[0].Data; row.Fields[1].Data = paths[filename]; } } } catch (Exception ex) { Console.WriteLine("An error occured extracting the {0} binary table from the install package.", tableName); Console.WriteLine(ex.Message); } }
public override bool GetDiff(string diffInput1, string diffInput2, string[] options, TextWriter diffOutput, string linePrefix, IDiffEngineFactory diffFactory) { bool difference = false; InstallPackage db1, db2; if(IsMspPatch(diffInput1)) { string patchTargetDbFile = GetPatchTargetOption(options); if(patchTargetDbFile == null) patchTargetDbFile = diffInput2; string tempPatchedDbFile = Path.GetTempFileName(); File.Copy(patchTargetDbFile, tempPatchedDbFile, true); File.SetAttributes(tempPatchedDbFile, File.GetAttributes(tempPatchedDbFile) & ~System.IO.FileAttributes.ReadOnly); db1 = new InstallPackage(tempPatchedDbFile, DatabaseOpenMode.Direct); db1.ApplyPatch(new PatchPackage(diffInput1), null); db1.Commit(); } else { db1 = new InstallPackage(diffInput1, DatabaseOpenMode.ReadOnly); } if(IsMspPatch(diffInput2)) { string patchTargetDbFile = GetPatchTargetOption(options); if(patchTargetDbFile == null) patchTargetDbFile = diffInput1; string tempPatchedDbFile = Path.GetTempFileName(); File.Copy(patchTargetDbFile, tempPatchedDbFile, true); File.SetAttributes(tempPatchedDbFile, File.GetAttributes(tempPatchedDbFile) & ~System.IO.FileAttributes.ReadOnly); db2 = new InstallPackage(tempPatchedDbFile, DatabaseOpenMode.Direct); db2.ApplyPatch(new PatchPackage(diffInput2), null); db2.Commit(); } else { db2 = new InstallPackage(diffInput2, DatabaseOpenMode.ReadOnly); } if(GetSummaryInfoDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true; if(GetDatabaseDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true; if(GetStreamsDiff(db1, db2, options, diffOutput, linePrefix, diffFactory)) difference = true; db1.Close(); db2.Close(); try { if(IsMspPatch(diffInput1)) File.Delete(db1.FilePath); if(IsMspPatch(diffInput1)) File.Delete(db2.FilePath); } catch(IOException) { #if DEBUG Console.WriteLine("Could not delete temporary files {0} and {1}", db1.FilePath, db2.FilePath); #endif } if(IsMspPatch(diffInput1) && IsMspPatch(diffInput2)) { Database dbp1 = new Database(diffInput1, DatabaseOpenMode.ReadOnly); Database dbp2 = new Database(diffInput2, DatabaseOpenMode.ReadOnly); if(GetStreamsDiff(dbp1, dbp2, options, diffOutput, linePrefix, diffFactory)) difference = true; dbp1.Close(); dbp2.Close(); } return difference; }
static ICollection<string> FindFileKeys(InstallPackage pkg, ICollection<string> fileNames) { List<string> fileKeys = null; if(fileNames != null) { fileKeys = new List<string>(); 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; }
/// <summary> /// Merges ICE cubes into the database <paramref name="item"/> and executes selected ICEs. /// </summary> /// <param name="item">The database to validate.</param> protected override void ProcessItem(PSObject item) { // Get the item path and set the current context. string path = item.GetPropertyValue<string>("PSPath"); path = this.SessionState.Path.GetUnresolvedProviderPathFromPSPath(path); this.CurrentPath = path; // Copy the database to a writable location and open. string copy = this.Copy(path); using (var db = new InstallPackage(copy, DatabaseOpenMode.Direct)) { // Apply any patches or transforms before otherwise modifying. this.ApplyTransforms(db); // Copy the ProductCode and drop the Property table to avoid opening an installed product. bool hasProperty = db.IsTablePersistent("Property"); string productCode = null; if (hasProperty) { productCode = db.ExecutePropertyQuery("ProductCode"); } // Merge the ICE cubes and fix up the database if needed. this.MergeCubes(db); if (!hasProperty) { db.Execute("DROP TABLE `Property`"); } var included = new List<WildcardPattern>(); if (null != this.Include) { Array.ForEach(this.Include, pattern => included.Add(new WildcardPattern(pattern))); } var excluded = new List<WildcardPattern>(); if (null != this.Exclude) { Array.ForEach(this.Exclude, pattern => excluded.Add(new WildcardPattern(pattern))); } // Get all the ICE actions in the database that are not excluded. var actions = new List<string>(); foreach (var action in db.ExecuteStringQuery("SELECT `Action` FROM `_ICESequence` ORDER BY `Sequence`")) { if (!action.Match(excluded)) { actions.Add(action); } } // Remove any actions not explicitly included. if (0 < included.Count) { for (int i = actions.Count - 1; 0 <= i; --i) { if (!actions[i].Match(included)) { actions.RemoveAt(i); } } } // Open a session with the database. using (var session = Installer.OpenPackage(db, false)) { // Put the original ProductCode back. if (!string.IsNullOrEmpty(productCode)) { db.Execute("DELETE FROM `Property` WHERE `Property` = 'ProductCode'"); db.Execute("INSERT INTO `Property` (`Property`, `Value`) VALUES ('ProductCode', '{0}')", productCode); } // Now execute all the remaining actions in order. foreach (string action in actions) { try { session.DoAction(action); this.Flush(); } catch (InstallerException ex) { using (var pse = new PSInstallerException(ex)) { if (null != pse.ErrorRecord) { this.WriteError(pse.ErrorRecord); } } } } } } }
private void MergeCubes(InstallPackage db) { if (!this.NoDefault) { string darice = ComponentSearcher.Find(ComponentSearcher.KnownComponent.Darice); if (!string.IsNullOrEmpty(darice)) { this.MergeCube(db, darice); } else { this.WriteWarning(Resources.Error_DefaultCubNotFound); } } if (null != this.AdditionalCube) { foreach (string cube in this.ResolveFiles(this.AdditionalCube)) { this.MergeCube(db, cube); } db.Commit(); } }
protected Database OpenDatabase(string path) { var type = FileInfo.GetFileTypeInternal(path); if (FileType.Package == type) { var db = new InstallPackage(path, DatabaseOpenMode.ReadOnly); this.ApplyTransforms(db); return db; } else if (FileType.Patch == type) { return new PatchPackage(path); } else { var message = string.Format(Resources.Error_InvalidStorage, path); var ex = new PSNotSupportedException(message); if (null != ex.ErrorRecord) { base.WriteError(ex.ErrorRecord); } return null; } }