// 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; using System.Collections.Generic; /// /// Accessor for information about features within the context of an installation session. /// public sealed class FeatureInfoCollection : ICollection { private Session session; internal FeatureInfoCollection(Session session) { this.session = session; } /// /// Gets information about a feature within the context of an installation session. /// /// name of the feature /// feature object public FeatureInfo this[string feature] { get { return new FeatureInfo(this.session, feature); } } void ICollection.Add(FeatureInfo item) { throw new InvalidOperationException(); } void ICollection.Clear() { throw new InvalidOperationException(); } /// /// Checks if the collection contains a feature. /// /// name of the feature /// true if the feature is in the collection, else false public bool Contains(string feature) { return this.session.Database.CountRows( "Feature", "`Feature` = '" + feature + "'") == 1; } bool ICollection.Contains(FeatureInfo item) { return item != null && this.Contains(item.Name); } /// /// Copies the features into an array. /// /// array that receives the features /// offset into the array public void CopyTo(FeatureInfo[] array, int arrayIndex) { foreach (FeatureInfo feature in this) { array[arrayIndex++] = feature; } } /// /// Gets the number of features defined for the product. /// public int Count { get { return this.session.Database.CountRows("Feature"); } } bool ICollection.IsReadOnly { get { return true; } } bool ICollection.Remove(FeatureInfo item) { throw new InvalidOperationException(); } /// /// Enumerates the features in the collection. /// /// an enumerator over all features in the collection public IEnumerator GetEnumerator() { using (View featureView = this.session.Database.OpenView( "SELECT `Feature` FROM `Feature`")) { featureView.Execute(); foreach (Record featureRec in featureView) using (featureRec) { string feature = featureRec.GetString(1); yield return new FeatureInfo(this.session, feature); } } } IEnumerator IEnumerable.GetEnumerator() { return this.GetEnumerator(); } } /// /// Provides access to information about a feature within the context of an installation session. /// public class FeatureInfo { private Session session; private string name; internal FeatureInfo(Session session, string name) { this.session = session; this.name = name; } /// /// Gets the name of the feature (primary key in the Feature table). /// public string Name { get { return this.name; } } /// /// Gets the current install state of the feature. /// /// the Session handle is invalid /// an unknown feature was requested ///

/// Win32 MSI API: /// MsiGetFeatureState ///

public InstallState CurrentState { get { int installState, actionState; uint ret = RemotableNativeMethods.MsiGetFeatureState((int) this.session.Handle, this.name, out installState, out actionState); if (ret != 0) { if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) { throw InstallerException.ExceptionFromReturnCode(ret, this.name); } else { throw InstallerException.ExceptionFromReturnCode(ret); } } if (installState == (int) InstallState.Advertised) { return InstallState.Advertised; } return (InstallState) installState; } } /// /// Gets or sets the action state of the feature. /// /// the Session handle is invalid /// an unknown feature was requested ///

/// When changing the feature action, the action state of all the Components linked to the changed /// Feature records are also updated appropriately, based on the new feature Select state. /// All Features can be configured at once by specifying the keyword ALL instead of a specific feature name. ///

/// Win32 MSI APIs: /// MsiGetFeatureState, /// MsiSetFeatureState ///

public InstallState RequestState { get { int installState, actionState; uint ret = RemotableNativeMethods.MsiGetFeatureState((int) this.session.Handle, this.name, out installState, out actionState); if (ret != 0) { if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) { throw InstallerException.ExceptionFromReturnCode(ret, this.name); } else { throw InstallerException.ExceptionFromReturnCode(ret); } } return (InstallState) actionState; } set { uint ret = RemotableNativeMethods.MsiSetFeatureState((int) this.session.Handle, this.name, (int) value); if (ret != 0) { if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) { throw InstallerException.ExceptionFromReturnCode(ret, this.name); } else { throw InstallerException.ExceptionFromReturnCode(ret); } } } } /// /// Gets a list of valid installation states for the feature. /// /// the Session handle is invalid /// an unknown feature was requested ///

/// Win32 MSI API: /// MsiGetFeatureValidStates ///

public ICollection ValidStates { get { List states = new List(); uint installState; uint ret = RemotableNativeMethods.MsiGetFeatureValidStates((int) this.session.Handle, this.name, out installState); if (ret != 0) { if (ret == (uint) NativeMethods.Error.UNKNOWN_FEATURE) { throw InstallerException.ExceptionFromReturnCode(ret, this.name); } else { throw InstallerException.ExceptionFromReturnCode(ret); } } for (int i = 1; i <= (int) InstallState.Default; i++) { if (((int) installState & (1 << i)) != 0) { states.Add((InstallState) i); } } return states.AsReadOnly(); } } /// /// Gets or sets the attributes of the feature. /// /// the Session handle is invalid /// an unknown feature was requested ///

/// Win32 MSI APIs: /// MsiGetFeatureInfo, /// MsiSetFeatureAttributes ///

/// Since the lpAttributes paramter of /// MsiGetFeatureInfo /// does not contain an equivalent flag for , this flag will /// not be retrieved. ///

/// Since the dwAttributes parameter of /// MsiSetFeatureAttributes /// does not contain an equivalent flag for , the presence /// of this flag will be ignored. ///

public FeatureAttributes Attributes { get { FeatureAttributes attributes; uint titleBufSize = 0; uint descBufSize = 0; uint attr; uint ret = NativeMethods.MsiGetFeatureInfo( (int) this.session.Handle, this.name, out attr, null, ref titleBufSize, null, ref descBufSize); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } // Values for attributes that MsiGetFeatureInfo returns are // double the values in the Attributes column of the Feature Table. attributes = (FeatureAttributes) (attr >> 1); // MsiGetFeatureInfo MSDN documentation indicates // NOUNSUPPORTEDADVERTISE is 32. Conversion above changes this to 16 // which is UIDisallowAbsent. MsiGetFeatureInfo isn't documented to // return an attribute for 'UIDisallowAbsent', so if UIDisallowAbsent // is set, change it to NoUnsupportedAdvertise which then maps correctly // to NOUNSUPPORTEDADVERTISE. if ((attributes & FeatureAttributes.UIDisallowAbsent) == FeatureAttributes.UIDisallowAbsent) { attributes &= ~FeatureAttributes.UIDisallowAbsent; attributes |= FeatureAttributes.NoUnsupportedAdvertise; } return attributes; } set { // MsiSetFeatureAttributes doesn't indicate UIDisallowAbsent is valid // so remove it. FeatureAttributes attributes = value; attributes &= ~FeatureAttributes.UIDisallowAbsent; // Values for attributes that MsiSetFeatureAttributes uses are // double the values in the Attributes column of the Feature Table. uint attr = ((uint) attributes) << 1; // MsiSetFeatureAttributes MSDN documentation indicates // NOUNSUPPORTEDADVERTISE is 32. Conversion above changes this to 64 // which is undefined. Change this back to 32. uint noUnsupportedAdvertiseDbl = ((uint)FeatureAttributes.NoUnsupportedAdvertise) << 1; if ((attr & noUnsupportedAdvertiseDbl) == noUnsupportedAdvertiseDbl) { attr &= ~noUnsupportedAdvertiseDbl; attr |= (uint) FeatureAttributes.NoUnsupportedAdvertise; } uint ret = RemotableNativeMethods.MsiSetFeatureAttributes((int) this.session.Handle, this.name, attr); if (ret != (uint)NativeMethods.Error.SUCCESS) { if (ret == (uint)NativeMethods.Error.UNKNOWN_FEATURE) { throw InstallerException.ExceptionFromReturnCode(ret, this.name); } else { throw InstallerException.ExceptionFromReturnCode(ret); } } } } /// /// Gets the title of the feature. /// /// the Session handle is invalid /// an unknown feature was requested ///

/// Win32 MSI API: /// MsiGetFeatureInfo ///

public string Title { get { StringBuilder titleBuf = new StringBuilder(80); uint titleBufSize = (uint) titleBuf.Capacity; uint descBufSize = 0; uint attr; uint ret = NativeMethods.MsiGetFeatureInfo( (int) this.session.Handle, this.name, out attr, titleBuf, ref titleBufSize, null, ref descBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { titleBuf.Capacity = (int) ++titleBufSize; ret = NativeMethods.MsiGetFeatureInfo( (int) this.session.Handle, this.name, out attr, titleBuf, ref titleBufSize, null, ref descBufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return titleBuf.ToString(); } } /// /// Gets the description of the feature. /// /// the Session handle is invalid /// an unknown feature was requested ///

/// Win32 MSI API: /// MsiGetFeatureInfo ///

public string Description { get { StringBuilder descBuf = new StringBuilder(256); uint titleBufSize = 0; uint descBufSize = (uint) descBuf.Capacity; uint attr; uint ret = NativeMethods.MsiGetFeatureInfo( (int) this.session.Handle, this.name, out attr, null, ref titleBufSize, descBuf, ref descBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { descBuf.Capacity = (int) ++descBufSize; ret = NativeMethods.MsiGetFeatureInfo( (int) this.session.Handle, this.name, out attr, null, ref titleBufSize, descBuf, ref descBufSize); } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return descBuf.ToString(); } } /// /// Calculates the disk space required by the feature and its selected children and parent features. /// /// If true, the parent features are included in the cost. /// If true, the child features are included in the cost. /// Specifies the installation state. /// The disk space requirement in bytes. ///

/// Win32 MSI API: /// MsiGetFeatureCost ///

public long GetCost(bool includeParents, bool includeChildren, InstallState installState) { const int MSICOSTTREE_CHILDREN = 1; const int MSICOSTTREE_PARENTS = 2; int cost; uint ret = RemotableNativeMethods.MsiGetFeatureCost( (int) this.session.Handle, this.name, (includeParents ? MSICOSTTREE_PARENTS : 0) | (includeChildren ? MSICOSTTREE_CHILDREN : 0), (int) installState, out cost); if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } return cost * 512L; } } }