mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-02-17 21:47:52 +00:00
526 lines
19 KiB
C#
526 lines
19 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.Globalization;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
/// <summary>
|
|
/// A list of sources for an installed product or patch.
|
|
/// </summary>
|
|
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
|
|
public class SourceList : ICollection<string>
|
|
{
|
|
private Installation installation;
|
|
private SourceMediaList mediaList;
|
|
|
|
internal SourceList(Installation installation)
|
|
{
|
|
this.installation = installation;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the list of disks registered for the media source of
|
|
/// the patch or product installation.
|
|
/// </summary>
|
|
public SourceMediaList MediaList
|
|
{
|
|
get
|
|
{
|
|
if (this.mediaList == null)
|
|
{
|
|
this.mediaList = new SourceMediaList(this.installation);
|
|
}
|
|
return this.mediaList;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the number of network and URL sources in the list.
|
|
/// </summary>
|
|
public int Count
|
|
{
|
|
get
|
|
{
|
|
int count = 0;
|
|
IEnumerator<string> e = this.GetEnumerator();
|
|
while (e.MoveNext())
|
|
{
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets a boolean value indicating whether the list is read-only.
|
|
/// A SourceList is never read-only.
|
|
/// </summary>
|
|
/// <value>read-only status of the list</value>
|
|
public bool IsReadOnly
|
|
{
|
|
get { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a network or URL source to the source list of the installed product.
|
|
/// </summary>
|
|
/// <param name="item">Path to the source to be added. This parameter is
|
|
/// expected to contain only the path without the filename.</param>
|
|
/// <remarks><p>
|
|
/// If this method is called with a new source, the installer adds the source
|
|
/// to the end of the source list.
|
|
/// </p><p>
|
|
/// If this method is called with a source already existing in the source
|
|
/// list, it has no effect.
|
|
/// </p><p>
|
|
/// Win32 MSI APIs:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistaddsource.asp">MsiSourceListAddSource</a>,
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistaddsourceex.asp">MsiSourceListAddSourceEx</a>
|
|
/// </p></remarks>
|
|
/// <seealso cref="Insert"/>
|
|
public void Add(string item)
|
|
{
|
|
if (!this.Contains(item))
|
|
{
|
|
this.Insert(item, 0);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds or reorders a network or URL source for the product or patch.
|
|
/// </summary>
|
|
/// <param name="item">Path to the source to be added. This parameter is
|
|
/// expected to contain only the path without the filename.</param>
|
|
/// <param name="index">Specifies the priority order in which the source
|
|
/// will be inserted</param>
|
|
/// <remarks><p>
|
|
/// If this method is called with a new source and <paramref name="index"/>
|
|
/// is set to 0, the installer adds the source to the end of the source list.
|
|
/// </p><p>
|
|
/// If this method is called with a source already existing in the source
|
|
/// list and <paramref name="index"/> is set to 0, the installer retains the
|
|
/// source's existing index.
|
|
/// </p><p>
|
|
/// If the method is called with an existing source in the source list
|
|
/// and <paramref name="index"/> is set to a non-zero value, the source is
|
|
/// removed from its current location in the list and inserted at the position
|
|
/// specified by Index, before any source that already exists at that position.
|
|
/// </p><p>
|
|
/// If the method is called with a new source and Index is set to a
|
|
/// non-zero value, the source is inserted at the position specified by
|
|
/// <paramref name="index"/>, before any source that already exists at
|
|
/// that position. The index value for all sources in the list after the
|
|
/// index specified by Index are updated to ensure unique index values and
|
|
/// the pre-existing order is guaranteed to remain unchanged.
|
|
/// </p><p>
|
|
/// If <paramref name="index"/> is greater than the number of sources
|
|
/// in the list, the source is placed at the end of the list with an index
|
|
/// value one larger than any existing source.
|
|
/// </p><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistaddsourceex.asp">MsiSourceListAddSourceEx</a>
|
|
/// </p></remarks>
|
|
public void Insert(string item, int index)
|
|
{
|
|
if (item == null)
|
|
{
|
|
throw new ArgumentNullException("item");
|
|
}
|
|
|
|
NativeMethods.SourceType type = item.Contains("://") ? NativeMethods.SourceType.Url : NativeMethods.SourceType.Network;
|
|
|
|
uint ret = NativeMethods.MsiSourceListAddSourceEx(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) type | (uint) this.installation.InstallationType,
|
|
item,
|
|
(uint) index);
|
|
if (ret != 0)
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Clears sources of all types: network, url, and media.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearall.asp">MsiSourceListClearAll</a>
|
|
/// </p></remarks>
|
|
public void Clear()
|
|
{
|
|
this.ClearSourceType(NativeMethods.SourceType.Url);
|
|
this.ClearSourceType(NativeMethods.SourceType.Network);
|
|
this.MediaList.Clear();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all network sources from the list. URL sources are not affected.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearallex.asp">MsiSourceListClearAllEx</a>
|
|
/// </p></remarks>
|
|
public void ClearNetworkSources()
|
|
{
|
|
this.ClearSourceType(NativeMethods.SourceType.Network);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes all URL sources from the list. Network sources are not affected.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearallex.asp">MsiSourceListClearAllEx</a>
|
|
/// </p></remarks>
|
|
public void ClearUrlSources()
|
|
{
|
|
this.ClearSourceType(NativeMethods.SourceType.Url);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if the specified source exists in the list.
|
|
/// </summary>
|
|
/// <param name="item">case-insensitive source to look for</param>
|
|
/// <returns>true if the source exists in the list, false otherwise</returns>
|
|
public bool Contains(string item)
|
|
{
|
|
if (String.IsNullOrEmpty(item))
|
|
{
|
|
throw new ArgumentNullException("item");
|
|
}
|
|
|
|
foreach (string s in this)
|
|
{
|
|
if (s.Equals(item, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Copies the network and URL sources from this list into an array.
|
|
/// </summary>
|
|
/// <param name="array">destination array to be filed</param>
|
|
/// <param name="arrayIndex">offset into the destination array where copying begins</param>
|
|
public void CopyTo(string[] array, int arrayIndex)
|
|
{
|
|
foreach (string source in this)
|
|
{
|
|
array[arrayIndex++] = source;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a network or URL source.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistclearsource.asp">MsiSourceListClearSource</a>
|
|
/// </p></remarks>
|
|
public bool Remove(string item)
|
|
{
|
|
if (String.IsNullOrEmpty(item))
|
|
{
|
|
throw new ArgumentNullException("item");
|
|
}
|
|
|
|
NativeMethods.SourceType type = item.Contains("://") ? NativeMethods.SourceType.Url : NativeMethods.SourceType.Network;
|
|
|
|
uint ret = NativeMethods.MsiSourceListClearSource(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) type | (uint) this.installation.InstallationType,
|
|
item);
|
|
if (ret != 0)
|
|
{
|
|
// TODO: Figure out when to return false.
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumerates the network and URL sources in the source list of the patch or product installation.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistenumsources.asp">MsiSourceListEnumSources</a>
|
|
/// </p></remarks>
|
|
public IEnumerator<string> GetEnumerator()
|
|
{
|
|
StringBuilder sourceBuf = new StringBuilder(256);
|
|
uint sourceBufSize = (uint) sourceBuf.Capacity;
|
|
for (uint i = 0; true; i++)
|
|
{
|
|
uint ret = this.EnumSources(sourceBuf, i, NativeMethods.SourceType.Network);
|
|
if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS)
|
|
{
|
|
break;
|
|
}
|
|
else if (ret != 0)
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
else
|
|
{
|
|
yield return sourceBuf.ToString();
|
|
}
|
|
}
|
|
|
|
for (uint i = 0; true; i++)
|
|
{
|
|
uint ret = this.EnumSources(sourceBuf, i, NativeMethods.SourceType.Url);
|
|
if (ret == (uint) NativeMethods.Error.NO_MORE_ITEMS)
|
|
{
|
|
break;
|
|
}
|
|
else if (ret != 0)
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
else
|
|
{
|
|
yield return sourceBuf.ToString();
|
|
}
|
|
}
|
|
}
|
|
|
|
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
|
|
{
|
|
return this.GetEnumerator();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Forces the installer to search the source list for a valid
|
|
/// source the next time a source is required. For example, when the
|
|
/// installer performs an installation or reinstallation, or when it
|
|
/// requires the path for a component that is set to run from source.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// Win32 MSI APIs:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistforceresolution.asp">MsiSourceListForceResolution</a>,
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistforceresolutionex.asp">MsiSourceListForceResolutionEx</a>
|
|
/// </p></remarks>
|
|
public void ForceResolution()
|
|
{
|
|
uint ret = NativeMethods.MsiSourceListForceResolutionEx(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) this.installation.InstallationType);
|
|
if (ret != 0)
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the path relative to the root of the installation media.
|
|
/// </summary>
|
|
public string MediaPackagePath
|
|
{
|
|
get
|
|
{
|
|
return this["MediaPackagePath"];
|
|
}
|
|
set
|
|
{
|
|
this["MediaPackagePath"] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the prompt template that is used when prompting the user
|
|
/// for installation media.
|
|
/// </summary>
|
|
public string DiskPrompt
|
|
{
|
|
get
|
|
{
|
|
return this["DiskPrompt"];
|
|
}
|
|
set
|
|
{
|
|
this["DiskPrompt"] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the most recently used source location for the product.
|
|
/// </summary>
|
|
public string LastUsedSource
|
|
{
|
|
get
|
|
{
|
|
return this["LastUsedSource"];
|
|
}
|
|
set
|
|
{
|
|
this["LastUsedSource"] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets the name of the Windows Installer package or patch package
|
|
/// on the source.
|
|
/// </summary>
|
|
public string PackageName
|
|
{
|
|
get
|
|
{
|
|
return this["PackageName"];
|
|
}
|
|
set
|
|
{
|
|
this["PackageName"] = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets the type of the last-used source.
|
|
/// </summary>
|
|
/// <remarks><p>
|
|
/// <ul>
|
|
/// <li>"n" = network location</li>
|
|
/// <li>"u" = URL location</li>
|
|
/// <li>"m" = media location</li>
|
|
/// <li>(empty string) = no last used source</li>
|
|
/// </ul>
|
|
/// </p></remarks>
|
|
public string LastUsedType
|
|
{
|
|
get
|
|
{
|
|
return this["LastUsedType"];
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gets or sets source list information properties of a product or patch installation.
|
|
/// </summary>
|
|
/// <param name="property">The source list information property name.</param>
|
|
/// <exception cref="ArgumentOutOfRangeException">An unknown product, patch, or property was requested</exception>
|
|
/// <remarks><p>
|
|
/// Win32 MSI API:
|
|
/// <a href="http://msdn.microsoft.com/library/en-us/msi/setup/msisourcelistgetinfo.asp">MsiSourceListGetInfo</a>
|
|
/// </p></remarks>
|
|
public string this[string property]
|
|
{
|
|
get
|
|
{
|
|
StringBuilder buf = new StringBuilder("");
|
|
uint bufSize = 0;
|
|
uint ret = NativeMethods.MsiSourceListGetInfo(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) this.installation.InstallationType,
|
|
property,
|
|
buf,
|
|
ref bufSize);
|
|
if (ret != 0)
|
|
{
|
|
if (ret == (uint) NativeMethods.Error.MORE_DATA)
|
|
{
|
|
buf.Capacity = (int) ++bufSize;
|
|
ret = NativeMethods.MsiSourceListGetInfo(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) this.installation.InstallationType,
|
|
property,
|
|
buf,
|
|
ref bufSize);
|
|
}
|
|
|
|
if (ret != 0)
|
|
{
|
|
if (ret == (uint) NativeMethods.Error.UNKNOWN_PRODUCT ||
|
|
ret == (uint) NativeMethods.Error.UNKNOWN_PROPERTY)
|
|
{
|
|
throw new ArgumentOutOfRangeException("property");
|
|
}
|
|
else
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
}
|
|
}
|
|
return buf.ToString();
|
|
}
|
|
set
|
|
{
|
|
uint ret = NativeMethods.MsiSourceListSetInfo(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) this.installation.InstallationType,
|
|
property,
|
|
value);
|
|
if (ret != 0)
|
|
{
|
|
if (ret == (uint) NativeMethods.Error.UNKNOWN_PRODUCT ||
|
|
ret == (uint) NativeMethods.Error.UNKNOWN_PROPERTY)
|
|
{
|
|
throw new ArgumentOutOfRangeException("property");
|
|
}
|
|
else
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void ClearSourceType(NativeMethods.SourceType type)
|
|
{
|
|
uint ret = NativeMethods.MsiSourceListClearAllEx(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) type | (uint) this.installation.InstallationType);
|
|
if (ret != 0)
|
|
{
|
|
throw InstallerException.ExceptionFromReturnCode(ret);
|
|
}
|
|
}
|
|
|
|
private uint EnumSources(StringBuilder sourceBuf, uint i, NativeMethods.SourceType sourceType)
|
|
{
|
|
int enumType = (this.installation.InstallationType | (int) sourceType);
|
|
uint sourceBufSize = (uint) sourceBuf.Capacity;
|
|
uint ret = NativeMethods.MsiSourceListEnumSources(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) enumType,
|
|
i,
|
|
sourceBuf,
|
|
ref sourceBufSize);
|
|
if (ret == (uint) NativeMethods.Error.MORE_DATA)
|
|
{
|
|
sourceBuf.Capacity = (int) ++sourceBufSize;
|
|
ret = NativeMethods.MsiSourceListEnumSources(
|
|
this.installation.InstallationCode,
|
|
this.installation.UserSid,
|
|
this.installation.Context,
|
|
(uint) enumType,
|
|
i,
|
|
sourceBuf,
|
|
ref sourceBufSize);
|
|
}
|
|
return ret;
|
|
}
|
|
}
|
|
}
|