Files
BinaryObjectScanner/BurnOutSharp/External/WixToolset/WixToolset.Dtf.WindowsInstaller/ComponentInstallation.cs
2021-03-02 13:09:15 -08:00

383 lines
16 KiB
C#

// 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;
/// <summary>
/// Represents an instance of a registered component.
/// </summary>
public class ComponentInstallation : InstallationPart
{
/// <summary>
/// Gets the set of installed components for all products.
/// </summary>
/// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
/// <remarks><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumcomponents.asp">MsiEnumComponents</a>
/// </p></remarks>
public static IEnumerable<ComponentInstallation> 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());
}
}
}
/// <summary>
/// Gets the set of installed components for products in the indicated context.
/// </summary>
/// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
/// <remarks><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/dd407947.aspx">MsiEnumComponentsEx</a>
/// </p></remarks>
public static IEnumerable<ComponentInstallation> 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);
}
/// <summary>
/// Creates a new ComponentInstallation, automatically detecting the
/// product that the component is a part of.
/// </summary>
/// <param name="componentCode">component GUID</param>
/// <remarks><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetproductcode.asp">MsiGetProductCode</a>
/// </p></remarks>
public ComponentInstallation(string componentCode)
: this(componentCode, ComponentInstallation.GetProductCode(componentCode))
{
}
/// <summary>
/// Creates a new ComponentInstallation, automatically detecting the
/// product that the component is a part of.
/// </summary>
/// <param name="componentCode">component GUID</param>
/// <param name="szUserSid">context user SID</param>
/// <param name="dwContext">user contexts</param>
public ComponentInstallation(string componentCode, string szUserSid, UserContexts dwContext)
: this(componentCode, ComponentInstallation.GetProductCode(componentCode, szUserSid, dwContext), szUserSid, dwContext)
{
}
/// <summary>
/// Creates a new ComponentInstallation for a component installed by a
/// specific product.
/// </summary>
/// <param name="componentCode">component GUID</param>
/// <param name="productCode">ProductCode GUID</param>
public ComponentInstallation(string componentCode, string productCode)
: this(componentCode, productCode, null, UserContexts.None)
{
}
/// <summary>
/// Creates a new ComponentInstallation for a component installed by a
/// specific product.
/// </summary>
/// <param name="componentCode">component GUID</param>
/// <param name="productCode">ProductCode GUID</param>
/// <param name="szUserSid">context user SID</param>
/// <param name="dwContext">user contexts</param>
public ComponentInstallation(string componentCode, string productCode, string szUserSid, UserContexts dwContext)
: base(componentCode, productCode, szUserSid, dwContext)
{
if (string.IsNullOrEmpty(componentCode))
{
throw new ArgumentNullException("componentCode");
}
}
/// <summary>
/// Gets the component code (GUID) of the component.
/// </summary>
public string ComponentCode
{
get
{
return this.Id;
}
}
/// <summary>
/// Gets all client products of a specified component.
/// </summary>
/// <returns>enumeration over all client products of the component</returns>
/// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
/// <remarks><p>
/// Because clients are not ordered, any new component has an arbitrary index.
/// This means that the property may return clients in any order.
/// </p><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumclients.asp">MsiEnumClients</a>,
/// <a href="http://msdn.microsoft.com/library/dd407946.aspx">MsiEnumClientsEx</a>
/// </p></remarks>
public IEnumerable<ProductInstallation> 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());
}
}
}
/// <summary>
/// Gets the installed state of a component.
/// </summary>
/// <returns>the installed state of the component, or InstallState.Unknown
/// if this component is not part of a product</returns>
/// <remarks><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetcomponentpath.asp">MsiGetComponentPath</a>,
/// <a href="http://msdn.microsoft.com/library/dd408006.aspx">MsiGetComponentPathEx</a>
/// </p></remarks>
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;
}
}
}
/// <summary>
/// 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.
/// </summary>
/// <returns>The file or registry keypath to the component, or null if the component is not available.</returns>
/// <exception cref="ArgumentException">An unknown product or component was specified</exception>
/// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
/// <remarks><p>
/// 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
/// </p><p>
/// Win32 MSI APIs:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msigetcomponentpath.asp">MsiGetComponentPath</a>,
/// <a href="http://msdn.microsoft.com/library/dd408006.aspx">MsiGetComponentPathEx</a>,
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msilocatecomponent.asp">MsiLocateComponent</a>
/// </p></remarks>
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;
}
}
}
/// <summary>
/// Gets the set of registered qualifiers for the component.
/// </summary>
/// <returns>Enumeration of the qulifiers for the component.</returns>
/// <exception cref="InstallerException">The installer configuration data is corrupt</exception>
/// <remarks><p>
/// Because qualifiers are not ordered, any new qualifier has an arbitrary index,
/// meaning the function can return qualifiers in any order.
/// </p><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumcomponentqualifiers.asp">MsiEnumComponentQualifiers</a>
/// </p></remarks>
public IEnumerable<ComponentInstallation.Qualifier> 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());
}
}
}
/// <summary>
/// Holds data about a component qualifier.
/// </summary>
/// <remarks><p>
/// Win32 MSI API:
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msienumcomponentqualifiers.asp">MsiEnumComponentQualifiers</a>
/// </p></remarks>
[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;
}
/// <summary>
/// Gets the qualifier code.
/// </summary>
public string QualifierCode
{
get
{
return this.qualifierCode;
}
}
/// <summary>
/// Gets the qualifier data.
/// </summary>
public string Data
{
get
{
return this.data;
}
}
}
}
}