From 3f583916719eeef598d10a5d4e14ef14f008243b Mon Sep 17 00:00:00 2001 From: Rob Mensching Date: Tue, 11 May 2021 07:36:37 -0700 Subject: Merge Dtf --- .../ComponentInstallation.cs | 382 +++++++++++++++++++++ 1 file changed, 382 insertions(+) create mode 100644 src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs (limited to 'src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs') diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs new file mode 100644 index 00000000..6d368899 --- /dev/null +++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs @@ -0,0 +1,382 @@ +// 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; + } + } + } + } +} -- cgit v1.2.3-55-g6feb