// 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 { using System; using System.Text; using System.Collections.Generic; using System.Globalization; using System.Diagnostics.CodeAnalysis; /// /// The Patch object represents a unique instance of a patch that has been /// registered or applied. /// public class PatchInstallation : Installation { /// /// Enumerates all patch installations on the system. /// /// Enumeration of patch objects. ///

/// Win32 MSI API: /// MsiEnumPatches ///

public static IEnumerable AllPatches { get { return PatchInstallation.GetPatches(null, null, null, UserContexts.All, PatchStates.All); } } /// /// Enumerates patch installations based on certain criteria. /// /// PatchCode (GUID) of the patch to be enumerated. Only /// instances of patches within the scope of the context specified by the /// and parameters will be /// enumerated. This parameter may be set to null to enumerate all patches in the specified /// context. /// ProductCode (GUID) product whose patches are to be /// enumerated. If non-null, patch enumeration is restricted to instances of this product /// within the specified context. If null, the patches for all products under the specified /// context are enumerated. /// Specifies a security identifier (SID) that restricts the context /// of enumeration. A SID value other than s-1-1-0 is considered a user SID and restricts /// enumeration to the current user or any user in the system. The special SID string /// s-1-1-0 (Everyone) specifies enumeration across all users in the system. This parameter /// can be set to null to restrict the enumeration scope to the current user. When /// must be null. /// Specifies the user context. /// The of patches to return. ///

/// Win32 MSI APIs: /// MsiEnumPatchesEx ///

public static IEnumerable GetPatches( string patchCode, string targetProductCode, string userSid, UserContexts context, PatchStates states) { StringBuilder buf = new StringBuilder(40); StringBuilder targetProductBuf = new StringBuilder(40); UserContexts targetContext; StringBuilder targetSidBuf = new StringBuilder(40); for (uint i = 0; ; i++) { uint targetSidBufSize = (uint) targetSidBuf.Capacity; uint ret = NativeMethods.MsiEnumPatchesEx( targetProductCode, userSid, context, (uint) states, i, buf, targetProductBuf, out targetContext, targetSidBuf, ref targetSidBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { targetSidBuf.Capacity = (int) ++targetSidBufSize; ret = NativeMethods.MsiEnumPatchesEx( targetProductCode, userSid, context, (uint) states, i, buf, targetProductBuf, out targetContext, targetSidBuf, ref targetSidBufSize); } if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) { break; } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } string thisPatchCode = buf.ToString(); if (patchCode == null || patchCode == thisPatchCode) { yield return new PatchInstallation( buf.ToString(), targetProductBuf.ToString(), targetSidBuf.ToString(), targetContext); } } } private string productCode; /// /// Creates a new object for accessing information about a patch installation on the current system. /// /// Patch code (GUID) of the patch. /// ProductCode (GUID) the patch has been applied to. /// This parameter may be null for patches that are registered only and not yet /// applied to any product. ///

/// All available user contexts will be queried. ///

public PatchInstallation(string patchCode, string productCode) : this(patchCode, productCode, null, UserContexts.All) { } /// /// Creates a new object for accessing information about a patch installation on the current system. /// /// Registered patch code (GUID) of the patch. /// ProductCode (GUID) the patch has been applied to. /// This parameter may be null for patches that are registered only and not yet /// applied to any product. /// The specific user, when working in a user context. This /// parameter may be null to indicate the current user. The parameter must be null /// when working in a machine context. /// The user context. The calling process must have administrative /// privileges to get information for a product installed for a user other than the /// current user. ///

/// If the is null, the Patch object may /// only be used to read and update the patch's SourceList information. ///

public PatchInstallation(string patchCode, string productCode, string userSid, UserContexts context) : base(patchCode, userSid, context) { if (String.IsNullOrEmpty(patchCode)) { throw new ArgumentNullException("patchCode"); } this.productCode = productCode; } /// /// Gets the patch code (GUID) of the patch. /// public string PatchCode { get { return this.InstallationCode; } } /// /// Gets the ProductCode (GUID) of the product. /// public string ProductCode { get { return this.productCode; } } /// /// Gets a value indicating whether this patch is currently installed. /// public override bool IsInstalled { get { return (this.State & PatchStates.Applied) != 0; } } /// /// Gets a value indicating whether this patch is marked as obsolte. /// [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Obsoleted")] public bool IsObsoleted { get { return (this.State & PatchStates.Obsoleted) != 0; } } /// /// Gets a value indicating whether this patch is present but has been /// superseded by a more recent installed patch. /// public bool IsSuperseded { get { return (this.State & PatchStates.Superseded) != 0; } } internal override int InstallationType { get { const int MSICODE_PATCH = 0x40000000; return MSICODE_PATCH; } } /// /// Gets the installation state of this instance of the patch. /// /// An unknown patch was requested /// The installer configuration data is corrupt public PatchStates State { get { string stateString = this["State"]; return (PatchStates) Int32.Parse(stateString, CultureInfo.InvariantCulture.NumberFormat); } } /// /// Gets the cached patch file that the product uses. /// public string LocalPackage { get { return this["LocalPackage"]; } } /// /// Gets the set of patch transforms that the last patch /// installation applied to the product. /// ///

/// This value may not be available for per-user, non-managed applications /// if the user is not logged on. ///

public string Transforms { get { // TODO: convert to IList? return this["Transforms"]; } } /// /// Gets the date and time when the patch is applied to the product. /// public DateTime InstallDate { get { try { return DateTime.ParseExact( this["InstallDate"], "yyyyMMdd", CultureInfo.InvariantCulture); } catch (FormatException) { return DateTime.MinValue; } } } /// /// True patch is marked as possible to uninstall from the product. /// ///

/// Even if this property is true, the installer can still block the /// uninstallation if this patch is required by another patch that /// cannot be uninstalled. ///

[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Uninstallable")] public bool Uninstallable { get { return this["Uninstallable"] == "1"; } } /// /// Get the registered display name for the patch. /// public string DisplayName { get { return this["DisplayName"]; } } /// /// Gets the registered support information URL for the patch. /// public Uri MoreInfoUrl { get { string value = this["MoreInfoURL"]; if (!String.IsNullOrEmpty(value)) { try { return new Uri(value); } catch (UriFormatException) { } } return null; } } /// /// Gets information about a specific patch installation. /// /// The property being retrieved; see remarks for valid properties. /// The property value, or an empty string if the property is not set for the patch. /// An unknown patch or property was requested /// The installer configuration data is corrupt ///

/// Win32 MSI APIs: /// MsiGetPatchInfo, /// MsiGetPatchInfoEx ///

public override string this[string propertyName] { get { StringBuilder buf = new StringBuilder(""); uint bufSize = 0; uint ret; if (this.Context == UserContexts.UserManaged || this.Context == UserContexts.UserUnmanaged || this.Context == UserContexts.Machine) { ret = NativeMethods.MsiGetPatchInfoEx( this.PatchCode, this.ProductCode, this.UserSid, this.Context, propertyName, buf, ref bufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { buf.Capacity = (int) ++bufSize; ret = NativeMethods.MsiGetPatchInfoEx( this.PatchCode, this.ProductCode, this.UserSid, this.Context, propertyName, buf, ref bufSize); } } else { ret = NativeMethods.MsiGetPatchInfo( this.PatchCode, propertyName, buf, ref bufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { buf.Capacity = (int) ++bufSize; ret = NativeMethods.MsiGetPatchInfo( this.PatchCode, propertyName, buf, ref bufSize); } } if (ret != 0) { return null; } return buf.ToString(); } } } }