public static void RegisterMSBuildPath()

in src/MSBuildLocator/MSBuildLocator.cs [165:280]


        public static void RegisterMSBuildPath(string[] msbuildSearchPaths)
        {
            if (msbuildSearchPaths.Length < 1)
            {
                throw new ArgumentException("Must provide at least one search path to RegisterMSBuildPath.");
            }

            List<ArgumentException> nullOrWhiteSpaceExceptions = new List<ArgumentException>();
            for (int i = 0; i < msbuildSearchPaths.Length; i++)
            {
                if (string.IsNullOrWhiteSpace(msbuildSearchPaths[i]))
                {
                    nullOrWhiteSpaceExceptions.Add(new ArgumentException($"Value at position {i+1} may not be null or whitespace", nameof(msbuildSearchPaths)));
                }
            }
            if (nullOrWhiteSpaceExceptions.Count > 0)
            {
                throw new AggregateException("Search paths for MSBuild assemblies cannot be null and must contain non-whitespace characters.", nullOrWhiteSpaceExceptions);
            }

            IEnumerable<string> paths = msbuildSearchPaths.Where(path => !Directory.Exists(path));
            if (paths.Any())
            {
                throw new AggregateException($"A directory or directories in \"{nameof(msbuildSearchPaths)}\" do not exist", paths.Select(path => new ArgumentException($"Directory \"{path}\" does not exist", nameof(msbuildSearchPaths))));
            }

            if (!CanRegister)
            {
                var loadedAssemblyList = string.Join(Environment.NewLine, LoadedMsBuildAssemblies.Select(a => a.GetName()));

                var error = $"{typeof(MSBuildLocator)}.{nameof(RegisterInstance)} was called, but MSBuild assemblies were already loaded." +
                    Environment.NewLine +
                    $"Ensure that {nameof(RegisterInstance)} is called before any method that directly references types in the Microsoft.Build namespace has been called." +
                    Environment.NewLine +
                    "This dependency arises from when a method is just-in-time compiled, so if it breaks even if the reference to a Microsoft.Build type has not been executed." +
                    Environment.NewLine +
                    "For more details, see aka.ms/RegisterMSBuildLocator" +
                    Environment.NewLine +
                    "Loaded MSBuild assemblies: " +
                    loadedAssemblyList;

                throw new InvalidOperationException(error);
            }

            // AssemblyResolve event can fire multiple times for the same assembly, so keep track of what's already been loaded.
            var loadedAssemblies = new Dictionary<string, Assembly>();

#if NET46
            // MSBuild can be loaded from the x86 or x64 folder. Before 17.0, it looked next to the executing assembly in some cases and constructed a path that assumed x86 in others.
            // This overrides the latter assumption to let it find the right MSBuild.
            foreach (string path in msbuildSearchPaths)
            {
                string msbuildExe = Path.Combine(path, "MSBuild.exe");
                if (File.Exists(msbuildExe))
                {
                    FileVersionInfo ver = FileVersionInfo.GetVersionInfo(msbuildExe);
                    if (ver.FileMajorPart < 17 || (ver.FileMajorPart == 17 && ver.FileMinorPart < 1))
                    {
                        if (Path.GetDirectoryName(msbuildExe).EndsWith(@"\amd64", StringComparison.OrdinalIgnoreCase))
                        {
                            msbuildExe = Path.Combine(path.Substring(0, path.Length - 6), "MSBuild.exe");
                        }
                        Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", msbuildExe);
                    }
                    break;
                }
            }
#endif

            // Saving the handler in a static field so it can be unregistered later.
#if NET46
            s_registeredHandler = (_, eventArgs) =>
            {
                var assemblyName = new AssemblyName(eventArgs.Name);
                return TryLoadAssembly(new AssemblyName(eventArgs.Name));
            };

            AppDomain.CurrentDomain.AssemblyResolve += s_registeredHandler;
#else
            s_registeredHandler = (_, assemblyName) => 
            {
                return TryLoadAssembly(assemblyName);
            };

            AssemblyLoadContext.Default.Resolving += s_registeredHandler;
#endif

            return;

            Assembly TryLoadAssembly(AssemblyName assemblyName)
            {
                // Assembly resolution is not thread-safe.
                lock (loadedAssemblies)
                {
                    if (loadedAssemblies.TryGetValue(assemblyName.FullName, out Assembly assembly))
                    {
                        return assembly;
                    }

                    // Look in the MSBuild folder for any unresolved reference. It may be a dependency
                    // of MSBuild or a task.
                    foreach (string msbuildPath in msbuildSearchPaths)
                    {
                        string targetAssembly = Path.Combine(msbuildPath, assemblyName.Name + ".dll");
                        if (File.Exists(targetAssembly))
                        {
                            assembly = Assembly.LoadFrom(targetAssembly);
                            loadedAssemblies.Add(assemblyName.FullName, assembly);
                            return assembly;
                        }
                    }

                    return null;
                }
            }
        }