// 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;
///
/// A list of sources for an installed product or patch.
///
[SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
public class SourceList : ICollection
{
private Installation installation;
private SourceMediaList mediaList;
internal SourceList(Installation installation)
{
this.installation = installation;
}
///
/// Gets the list of disks registered for the media source of
/// the patch or product installation.
///
public SourceMediaList MediaList
{
get
{
if (this.mediaList == null)
{
this.mediaList = new SourceMediaList(this.installation);
}
return this.mediaList;
}
}
///
/// Gets the number of network and URL sources in the list.
///
public int Count
{
get
{
int count = 0;
IEnumerator e = this.GetEnumerator();
while (e.MoveNext())
{
count++;
}
return count;
}
}
///
/// Gets a boolean value indicating whether the list is read-only.
/// A SourceList is never read-only.
///
/// read-only status of the list
public bool IsReadOnly
{
get { return false; }
}
///
/// Adds a network or URL source to the source list of the installed product.
///
/// Path to the source to be added. This parameter is
/// expected to contain only the path without the filename.
///
/// If this method is called with a new source, the installer adds the source
/// to the end of the source list.
///
/// If this method is called with a source already existing in the source
/// list, it has no effect.
///
/// Win32 MSI APIs:
/// MsiSourceListAddSource,
/// MsiSourceListAddSourceEx
///
///
public void Add(string item)
{
if (!this.Contains(item))
{
this.Insert(item, 0);
}
}
///
/// Adds or reorders a network or URL source for the product or patch.
///
/// Path to the source to be added. This parameter is
/// expected to contain only the path without the filename.
/// Specifies the priority order in which the source
/// will be inserted
///
/// If this method is called with a new source and
/// is set to 0, the installer adds the source to the end of the source list.
///
/// If this method is called with a source already existing in the source
/// list and is set to 0, the installer retains the
/// source's existing index.
///
/// If the method is called with an existing source in the source list
/// and 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.
///
/// 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
/// , 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.
///
/// If 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.
///
/// Win32 MSI API:
/// MsiSourceListAddSourceEx
///
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);
}
}
///
/// Clears sources of all types: network, url, and media.
///
///
/// Win32 MSI API:
/// MsiSourceListClearAll
///
public void Clear()
{
this.ClearSourceType(NativeMethods.SourceType.Url);
this.ClearSourceType(NativeMethods.SourceType.Network);
this.MediaList.Clear();
}
///
/// Removes all network sources from the list. URL sources are not affected.
///
///
/// Win32 MSI API:
/// MsiSourceListClearAllEx
///
public void ClearNetworkSources()
{
this.ClearSourceType(NativeMethods.SourceType.Network);
}
///
/// Removes all URL sources from the list. Network sources are not affected.
///
///
/// Win32 MSI API:
/// MsiSourceListClearAllEx
///
public void ClearUrlSources()
{
this.ClearSourceType(NativeMethods.SourceType.Url);
}
///
/// Checks if the specified source exists in the list.
///
/// case-insensitive source to look for
/// true if the source exists in the list, false otherwise
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;
}
///
/// Copies the network and URL sources from this list into an array.
///
/// destination array to be filed
/// offset into the destination array where copying begins
public void CopyTo(string[] array, int arrayIndex)
{
foreach (string source in this)
{
array[arrayIndex++] = source;
}
}
///
/// Removes a network or URL source.
///
///
/// Win32 MSI API:
/// MsiSourceListClearSource
///
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;
}
///
/// Enumerates the network and URL sources in the source list of the patch or product installation.
///
///
/// Win32 MSI API:
/// MsiSourceListEnumSources
///
public IEnumerator 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();
}
///
/// 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.
///
///
/// Win32 MSI APIs:
/// MsiSourceListForceResolution,
/// MsiSourceListForceResolutionEx
///
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);
}
}
///
/// Gets or sets the path relative to the root of the installation media.
///
public string MediaPackagePath
{
get
{
return this["MediaPackagePath"];
}
set
{
this["MediaPackagePath"] = value;
}
}
///
/// Gets or sets the prompt template that is used when prompting the user
/// for installation media.
///
public string DiskPrompt
{
get
{
return this["DiskPrompt"];
}
set
{
this["DiskPrompt"] = value;
}
}
///
/// Gets or sets the most recently used source location for the product.
///
public string LastUsedSource
{
get
{
return this["LastUsedSource"];
}
set
{
this["LastUsedSource"] = value;
}
}
///
/// Gets or sets the name of the Windows Installer package or patch package
/// on the source.
///
public string PackageName
{
get
{
return this["PackageName"];
}
set
{
this["PackageName"] = value;
}
}
///
/// Gets the type of the last-used source.
///
///
///
/// - "n" = network location
/// - "u" = URL location
/// - "m" = media location
/// - (empty string) = no last used source
///
///
public string LastUsedType
{
get
{
return this["LastUsedType"];
}
}
///
/// Gets or sets source list information properties of a product or patch installation.
///
/// The source list information property name.
/// An unknown product, patch, or property was requested
///
/// Win32 MSI API:
/// MsiSourceListGetInfo
///
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;
}
}
}