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