File: Engine\ToolsetRegistryReader.cs
Web Access
Project: ..\..\..\src\Deprecated\Engine\Microsoft.Build.Engine.csproj (Microsoft.Build.Engine)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
// THE ASSEMBLY BUILT FROM THIS SOURCE FILE HAS BEEN DEPRECATED FOR YEARS. IT IS BUILT ONLY TO PROVIDE
// BACKWARD COMPATIBILITY FOR API USERS WHO HAVE NOT YET MOVED TO UPDATED APIS. PLEASE DO NOT SEND PULL
// REQUESTS THAT CHANGE THIS FILE WITHOUT FIRST CHECKING WITH THE MAINTAINERS THAT THE FIX IS REQUIRED.
 
using System.Collections.Generic;
using error = Microsoft.Build.BuildEngine.Shared.ErrorUtilities;
 
namespace Microsoft.Build.BuildEngine
{
    /// <summary>
    /// Reads registry at the base key and returns a Dictionary keyed on ToolsVersion.
    /// Dictionary contains another dictionary of (property name, property value) pairs.
    /// If a registry value is not a string, this will throw a InvalidToolsetDefinitionException.
    /// An example of how the registry will look (note that the DefaultToolsVersion is per-MSBuild-version)
    /// [HKLM]\SOFTWARE\Microsoft
    ///   msbuild
    ///     3.5
    ///       @DefaultToolsVersion = 2.0
    ///     ToolsVersions
    ///       2.0
    ///         @MSBuildToolsPath = D:\SomeFolder
    ///       3.5
    ///         @MSBuildToolsPath = D:\SomeOtherFolder
    ///         @MSBuildBinPath = D:\SomeOtherFolder
    ///         @SomePropertyName = PropertyOtherValue
    /// </summary>
    internal class ToolsetRegistryReader : ToolsetReader
    {
        // Registry location for storing tools version dependent data for msbuild
        private const string msbuildRegistryPath = @"SOFTWARE\Microsoft\MSBuild";
 
        // Cached registry wrapper at root of the msbuild entries
        private RegistryKeyWrapper msbuildRegistryWrapper;
 
        /// <summary>
        /// Default constructor
        /// </summary>
        internal ToolsetRegistryReader()
            : this(new RegistryKeyWrapper(msbuildRegistryPath))
        {
        }
 
        /// <summary>
        /// Constructor overload accepting a registry wrapper for unit testing purposes only
        /// </summary>
        /// <param name="msbuildRegistryWrapper"></param>
        internal ToolsetRegistryReader(RegistryKeyWrapper msbuildRegistryWrapper)
        {
            error.VerifyThrowArgumentNull(msbuildRegistryWrapper, nameof(msbuildRegistryWrapper));
 
            this.msbuildRegistryWrapper = msbuildRegistryWrapper;
        }
 
        /// <summary>
        /// Returns the list of tools versions
        /// </summary>
        protected override IEnumerable<PropertyDefinition> ToolsVersions
        {
            get
            {
                string[] toolsVersionNames = new string[] { };
                try
                {
                    toolsVersionNames = msbuildRegistryWrapper.OpenSubKey("ToolsVersions").GetSubKeyNames();
                }
                catch (RegistryException ex)
                {
                    InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
                }
 
                foreach (string toolsVersionName in toolsVersionNames)
                {
                    yield return new PropertyDefinition(toolsVersionName, string.Empty, msbuildRegistryWrapper.Name + "\\ToolsVersions\\" + toolsVersionName);
                }
            }
        }
 
        /// <summary>
        /// Returns the default tools version, or null if none was specified
        /// </summary>
        protected override string DefaultToolsVersion
        {
            get
            {
                // We expect to find the DefaultToolsVersion value under a registry key named for our
                // version, e.g., "3.5"
                RegistryKeyWrapper defaultToolsVersionKey =
                    msbuildRegistryWrapper.OpenSubKey(Constants.AssemblyVersion);
 
                if (defaultToolsVersionKey != null)
                {
                    return GetValue(defaultToolsVersionKey, "DefaultToolsVersion");
                }
                else
                {
                    return null;
                }
            }
        }
 
        /// <summary>
        /// Provides an enumerator over property definitions for a specified tools version
        /// </summary>
        /// <param name="toolsVersion"></param>
        /// <returns></returns>
        protected override IEnumerable<PropertyDefinition> GetPropertyDefinitions(string toolsVersion)
        {
            RegistryKeyWrapper toolsVersionWrapper = null;
 
            try
            {
                toolsVersionWrapper = msbuildRegistryWrapper.OpenSubKey("ToolsVersions\\" + toolsVersion);
            }
            catch (RegistryException ex)
            {
                InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
            }
 
            foreach (string propertyName in toolsVersionWrapper.GetValueNames())
            {
                string propertyValue = null;
 
                if (propertyName?.Length == 0)
                {
                    InvalidToolsetDefinitionException.Throw("PropertyNameInRegistryHasZeroLength", toolsVersionWrapper.Name);
                }
 
                try
                {
                    propertyValue = GetValue(toolsVersionWrapper, propertyName);
                }
                catch (RegistryException ex)
                {
                    InvalidToolsetDefinitionException.Throw(ex, "RegistryReadError", ex.Source, ex.Message);
                }
 
                yield return new PropertyDefinition(propertyName, propertyValue, toolsVersionWrapper.Name + "@" + propertyName);
            }
        }
 
        /// <summary>
        /// Reads a string value from the specified registry key
        /// </summary>
        /// <param name="baseKeyWrapper">wrapper around key</param>
        /// <param name="valueName">name of the value</param>
        /// <returns>string data in the value</returns>
        private static string GetValue(RegistryKeyWrapper wrapper, string valueName)
        {
            if (wrapper.Exists())
            {
                object result = wrapper.GetValue(valueName);
 
                // RegistryKey.GetValue returns null if the value is not present
                // and String.Empty if the value is present and no data is defined.
                // We preserve this distinction, because a string property in the registry with
                // no value really has an empty string for a value (which is a valid property value)
                // rather than null for a value (which is an invalid property value)
                if (result != null)
                {
                    // Must be a value of string type
                    if (!(result is string))
                    {
                        InvalidToolsetDefinitionException.Throw("NonStringDataInRegistry", wrapper.Name + "@" + valueName);
                    }
 
                    return result.ToString();
                }
            }
 
            return null;
        }
    }
}