// 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.Diagnostics.CodeAnalysis; /// /// Represents an instance of a registered component. /// public class ComponentInstallation : InstallationPart { /// /// Gets the set of installed components for all products. /// /// The installer configuration data is corrupt ///

/// Win32 MSI API: /// MsiEnumComponents ///

public static IEnumerable AllComponents { get { StringBuilder buf = new StringBuilder(40); for (uint i = 0; true; i++) { uint ret = NativeMethods.MsiEnumComponents(i, buf); if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return new ComponentInstallation(buf.ToString()); } } } /// /// Gets the set of installed components for products in the indicated context. /// /// The installer configuration data is corrupt ///

/// Win32 MSI API: /// MsiEnumComponentsEx ///

public static IEnumerable Components(string szUserSid, UserContexts dwContext) { uint pcchSid = 32; StringBuilder szSid = new StringBuilder((int)pcchSid); StringBuilder buf = new StringBuilder(40); UserContexts installedContext; for (uint i = 0; true; i++) { uint ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); if (ret == (uint) NativeMethods.Error.MORE_DATA) { szSid.EnsureCapacity((int) ++pcchSid); ret = NativeMethods.MsiEnumComponentsEx(szUserSid, dwContext, i, buf, out installedContext, szSid, ref pcchSid); } if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return new ComponentInstallation(buf.ToString(), szSid.ToString(), installedContext); } } private static string GetProductCode(string component) { StringBuilder buf = new StringBuilder(40); uint ret = NativeMethods.MsiGetProductCode(component, buf); if (ret != 0) { return null; } return buf.ToString(); } private static string GetProductCode(string component, string szUserSid, UserContexts dwContext) { // TODO: We really need what would be MsiGetProductCodeEx, or at least a reasonable facimile thereof (something that restricts the search to the context defined by szUserSid & dwContext) return GetProductCode(component); } /// /// Creates a new ComponentInstallation, automatically detecting the /// product that the component is a part of. /// /// component GUID ///

/// Win32 MSI API: /// MsiGetProductCode ///

public ComponentInstallation(string componentCode) : this(componentCode, ComponentInstallation.GetProductCode(componentCode)) { } /// /// Creates a new ComponentInstallation, automatically detecting the /// product that the component is a part of. /// /// component GUID /// context user SID /// user contexts public ComponentInstallation(string componentCode, string szUserSid, UserContexts dwContext) : this(componentCode, ComponentInstallation.GetProductCode(componentCode, szUserSid, dwContext), szUserSid, dwContext) { } /// /// Creates a new ComponentInstallation for a component installed by a /// specific product. /// /// component GUID /// ProductCode GUID public ComponentInstallation(string componentCode, string productCode) : this(componentCode, productCode, null, UserContexts.None) { } /// /// Creates a new ComponentInstallation for a component installed by a /// specific product. /// /// component GUID /// ProductCode GUID /// context user SID /// user contexts public ComponentInstallation(string componentCode, string productCode, string szUserSid, UserContexts dwContext) : base(componentCode, productCode, szUserSid, dwContext) { if (string.IsNullOrEmpty(componentCode)) { throw new ArgumentNullException("componentCode"); } } /// /// Gets the component code (GUID) of the component. /// public string ComponentCode { get { return this.Id; } } /// /// Gets all client products of a specified component. /// /// enumeration over all client products of the component /// The installer configuration data is corrupt ///

/// Because clients are not ordered, any new component has an arbitrary index. /// This means that the property may return clients in any order. ///

/// Win32 MSI API: /// MsiEnumClients, /// MsiEnumClientsEx ///

public IEnumerable ClientProducts { get { StringBuilder buf = new StringBuilder(40); for (uint i = 0; true; i++) { uint chSid = 0; UserContexts installedContext; uint ret = this.Context == UserContexts.None ? NativeMethods.MsiEnumClients(this.ComponentCode, i, buf) : NativeMethods.MsiEnumClientsEx(this.ComponentCode, this.UserSid, this.Context, i, buf, out installedContext, null, ref chSid); if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS) break; else if (ret == (uint) NativeMethods.Error.UNKNOWN_COMPONENT) break; if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return new ProductInstallation(buf.ToString()); } } } /// /// Gets the installed state of a component. /// /// the installed state of the component, or InstallState.Unknown /// if this component is not part of a product ///

/// Win32 MSI API: /// MsiGetComponentPath, /// MsiGetComponentPathEx ///

public override InstallState State { get { if (this.ProductCode != null) { uint bufSize = 0; int installState = this.Context == UserContexts.None ? NativeMethods.MsiGetComponentPath( this.ProductCode, this.ComponentCode, null, ref bufSize) : NativeMethods.MsiGetComponentPathEx( this.ProductCode, this.ComponentCode, this.UserSid, this.Context, null, ref bufSize); return (InstallState) installState; } else { return InstallState.Unknown; } } } /// /// Gets the full path to an installed component. If the key path for the component is a /// registry key then the registry key is returned. /// /// The file or registry keypath to the component, or null if the component is not available. /// An unknown product or component was specified /// The installer configuration data is corrupt ///

/// If the component is a registry key, the registry roots are represented numerically. /// For example, a registry path of "HKEY_CURRENT_USER\SOFTWARE\Microsoft" would be returned /// as "01:\SOFTWARE\Microsoft". The registry roots returned are defined as follows: /// HKEY_CLASSES_ROOT=00, HKEY_CURRENT_USER=01, HKEY_LOCAL_MACHINE=02, HKEY_USERS=03, /// HKEY_PERFORMANCE_DATA=04 ///

/// Win32 MSI APIs: /// MsiGetComponentPath, /// MsiGetComponentPathEx, /// MsiLocateComponent ///

public string Path { get { StringBuilder buf = new StringBuilder(256); uint bufSize = (uint) buf.Capacity; InstallState installState; if (this.ProductCode != null) { installState = (this.Context == UserContexts.None) ? (InstallState) NativeMethods.MsiGetComponentPath( this.ProductCode, this.ComponentCode, buf, ref bufSize) : (InstallState) NativeMethods.MsiGetComponentPathEx( this.ProductCode, this.ComponentCode, this.UserSid, this.Context, buf, ref bufSize); if (installState == InstallState.MoreData) { buf.Capacity = (int) ++bufSize; installState = (this.Context == UserContexts.None) ? (InstallState) NativeMethods.MsiGetComponentPath( this.ProductCode, this.ComponentCode, buf, ref bufSize) : (InstallState) NativeMethods.MsiGetComponentPathEx( this.ProductCode, this.ComponentCode, this.UserSid, this.Context, buf, ref bufSize); } } else { installState = (InstallState) NativeMethods.MsiLocateComponent( this.ComponentCode, buf, ref bufSize); if (installState == InstallState.MoreData) { buf.Capacity = (int) ++bufSize; installState = (InstallState) NativeMethods.MsiLocateComponent( this.ComponentCode, buf, ref bufSize); } } switch (installState) { case InstallState.Local: case InstallState.Source: case InstallState.SourceAbsent: return buf.ToString(); default: return null; } } } /// /// Gets the set of registered qualifiers for the component. /// /// Enumeration of the qulifiers for the component. /// The installer configuration data is corrupt ///

/// Because qualifiers are not ordered, any new qualifier has an arbitrary index, /// meaning the function can return qualifiers in any order. ///

/// Win32 MSI API: /// MsiEnumComponentQualifiers ///

public IEnumerable Qualifiers { get { StringBuilder qualBuf = new StringBuilder(255); StringBuilder dataBuf = new StringBuilder(255); for (uint i = 0; ; i++) { uint qualBufSize = (uint) qualBuf.Capacity; uint dataBufSize = (uint) dataBuf.Capacity; uint ret = NativeMethods.MsiEnumComponentQualifiers( this.ComponentCode, i, qualBuf, ref qualBufSize, dataBuf, ref dataBufSize); if (ret == (uint) NativeMethods.Error.MORE_DATA) { qualBuf.Capacity = (int) ++qualBufSize; dataBuf.Capacity = (int) ++dataBufSize; ret = NativeMethods.MsiEnumComponentQualifiers( this.ComponentCode, i, qualBuf, ref qualBufSize, dataBuf, ref dataBufSize); } if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS || ret == (uint) NativeMethods.Error.UNKNOWN_COMPONENT) { break; } if (ret != 0) { throw InstallerException.ExceptionFromReturnCode(ret); } yield return new ComponentInstallation.Qualifier( qualBuf.ToString(), dataBuf.ToString()); } } } /// /// Holds data about a component qualifier. /// ///

/// Win32 MSI API: /// MsiEnumComponentQualifiers ///

[SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] [SuppressMessage("Microsoft.Performance", "CA1815:OverrideEqualsAndOperatorEqualsOnValueTypes")] public struct Qualifier { private string qualifierCode; private string data; internal Qualifier(string qualifierCode, string data) { this.qualifierCode = qualifierCode; this.data = data; } /// /// Gets the qualifier code. /// public string QualifierCode { get { return this.qualifierCode; } } /// /// Gets the qualifier data. /// public string Data { get { return this.data; } } } } }