// 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. namespace WixToolset.Dtf.WindowsInstaller.Package { using System; using System.IO; using System.Text; using System.Collections; using System.Globalization; using System.Runtime.InteropServices; /// /// Provides access to convenient properties and operations on a patch package (.MSP). /// public class PatchPackage : Database { /// /// Creates a new patch package object; opening the patch database in read-only mode. /// /// Path to the patch package (.MSP) /// The PatchPackage object only opens the patch database in read-only mode, because /// transforms (sub-storages) cannot be read if the database is open in read-write mode. public PatchPackage(string packagePath) : base(packagePath, (DatabaseOpenMode) ((int) DatabaseOpenMode.ReadOnly | 32)) // TODO: figure out what to do about DatabaseOpenMode.Patch { } /// /// Handle this event to receive status messages when operations are performed on the patch package. /// /// /// patchPackage.Message += new InstallPackageMessageHandler(Console.WriteLine); /// public event InstallPackageMessageHandler Message; /// /// Sends a message to the event-handler. /// /// Message string, containing 0 or more format items /// Items to be formatted protected void LogMessage(string format, params object[] args) { if(this.Message != null) { this.Message(format, args); } } /// /// Gets the patch code (GUID) of the patch package. /// /// /// The patch code is stored in the RevisionNumber field of the patch summary information. /// public string PatchCode { get { string guids = this.SummaryInfo.RevisionNumber; return guids.Substring(0, guids.IndexOf('}') + 1); } } /// /// Gets the list of patch codes that are replaced by this patch package. /// /// Array of replaced patch codes (GUIDs) /// /// The list of replaced patch codes is stored in the RevisionNumber field of the patch summary information. /// public string[] GetReplacedPatchCodes() { ArrayList patchCodeList = new ArrayList(); string guids = this.SummaryInfo.RevisionNumber; int thisGuid = guids.IndexOf('}') + 1; int nextGuid = guids.IndexOf('}', thisGuid) + 1; while(nextGuid > 0) { patchCodeList.Add(guids.Substring(thisGuid, (nextGuid - thisGuid))); thisGuid = nextGuid; nextGuid = guids.IndexOf('}', thisGuid) + 1; } return (string[]) patchCodeList.ToArray(typeof(string)); } /// /// Gets the list of product codes of products targeted by this patch package. /// /// Array of product codes (GUIDs) /// /// The list of target product codes is stored in the Template field of the patch summary information. /// public string[] GetTargetProductCodes() { string productList = this.SummaryInfo.Template; return productList.Split(';'); } /// /// Gets the names of the transforms included in the patch package. /// /// Array of transform names /// /// The returned list does not include the "patch special transforms" that are prefixed with "#" ///

The list of transform names is stored in the LastSavedBy field of the patch summary information.

///
public string[] GetTransforms() { return this.GetTransforms(false); } /// /// Gets the names of the transforms included in the patch package. /// /// Specifies whether to include the /// "patch special transforms" that are prefixed with "#" /// Array of transform names /// /// The list of transform names is stored in the LastSavedBy field of the patch summary information. /// public string[] GetTransforms(bool includeSpecialTransforms) { ArrayList transformArray = new ArrayList(); string transformList = this.SummaryInfo.LastSavedBy; foreach(string transform in transformList.Split(';', ':')) { if(transform.Length != 0 && (includeSpecialTransforms || !transform.StartsWith("#", StringComparison.Ordinal))) { transformArray.Add(transform); } } return (string[]) transformArray.ToArray(typeof(string)); } /// /// Gets information about the transforms included in the patch package. /// /// Array containing information about each transform /// /// The returned info does not include the "patch special transforms" that are prefixed with "#" /// public TransformInfo[] GetTransformsInfo() { return this.GetTransformsInfo(false); } /// /// Gets information about the transforms included in the patch package. /// /// Specifies whether to include the /// "patch special transforms" that are prefixed with "#" /// Array containing information about each transform public TransformInfo[] GetTransformsInfo(bool includeSpecialTransforms) { string[] transforms = this.GetTransforms(includeSpecialTransforms); ArrayList transformInfoArray = new ArrayList(transforms.Length); foreach(string transform in transforms) { transformInfoArray.Add(this.GetTransformInfo(transform)); } return (TransformInfo[]) transformInfoArray.ToArray(typeof(TransformInfo)); } /// /// Gets information about a transforms included in the patch package. /// /// Name of the transform to extract; this may optionally be a /// special transform prefixed by "#" /// Information about the transform public TransformInfo GetTransformInfo(string transform) { string tempTransformFile = null; try { tempTransformFile = Path.GetTempFileName(); this.ExtractTransform(transform, tempTransformFile); using(SummaryInfo transformSummInfo = new SummaryInfo(tempTransformFile, false)) { return new TransformInfo(transform, transformSummInfo); } } finally { if(tempTransformFile != null && File.Exists(tempTransformFile)) { File.Delete(tempTransformFile); } } } /// /// Analyzes the transforms included in the patch package to find the ones that /// are applicable to an install package. /// /// The install package to validate the transforms against /// Array of valid transform names /// /// 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. /// 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)); } /// /// Extracts a transform (.MST) from a patch package. /// /// Name of the transform to extract; this may optionally be a /// special transform prefixed by "#" /// Location where the transform will be extracted public void ExtractTransform(string transform, string extractFile) { using(View stgView = this.OpenView("SELECT `Name`, `Data` FROM `_Storages` WHERE `Name` = '{0}'", transform)) { stgView.Execute(); Record stgRec = stgView.Fetch(); if(stgRec == null) { this.LogMessage("Transform not found: {0}", transform); throw new InstallerException("Transform not found: " + transform); } using(stgRec) { stgRec.GetStream("Data", extractFile); } } } } }