Esempio n. 1
0
        private string GetVSInstallPath()
        {
            var dllLocation = Path.GetDirectoryName(typeof(CoverageLogger).GetTypeInfo().Assembly.Location);

            dllLocation = Path.Combine(dllLocation, IntPtr.Size == 4 ? SetupInteropx86 : SetupInteropx64);

            var setupInstance = ComOperations.CreateInstanceFrom(
                dllLocation,
                typeof(SetupConfiguration).GetTypeInfo().GUID,
                typeof(ISetupConfiguration).GetTypeInfo().GUID) as ISetupConfiguration;

            IEnumSetupInstances vsInstances = setupInstance?.EnumInstances();

            if (vsInstances != null)
            {
                vsInstances.Next(1, out ISetupInstance currentInstance, out int fetched);
                while (currentInstance != null && fetched == 1)
                {
                    string installRoot = Path.GetFullPath(currentInstance.GetInstallationPath());
                    if (!string.IsNullOrEmpty(installRoot))
                    {
                        return(installRoot);
                    }

                    vsInstances.Next(1, out currentInstance, out fetched);
                }
            }

            return(string.Empty);
        }
Esempio n. 2
0
        public static void PrintJson()
        {
            ISetupConfiguration query = new SetupConfiguration();

            ISetupConfiguration2 query2 = (ISetupConfiguration2)query;
            IEnumSetupInstances  e      = query2.EnumAllInstances();

            int pceltFetched;

            ISetupInstance2[] rgelt     = new ISetupInstance2[1];
            List <string>     instances = new List <string>();

            while (true)
            {
                e.Next(1, rgelt, out pceltFetched);
                if (pceltFetched <= 0)
                {
                    Console.WriteLine(String.Format("[{0}]", string.Join(",", instances.ToArray())));
                    return;
                }

                try
                {
                    instances.Add(InstanceJson(rgelt[0]));
                }
                catch (COMException)
                {
                    // Ignore instances that can't be queried.
                }
            }
        }
Esempio n. 3
0
        public static List <VSInstance> QueryEx(params string[] args)
        {
            List <VSInstance>    insts  = new List <VSInstance>();
            ISetupConfiguration  query  = new SetupConfiguration();
            ISetupConfiguration2 query2 = (ISetupConfiguration2)query;
            IEnumSetupInstances  e      = query2.EnumAllInstances();

            ISetupInstance2[] rgelt = new ISetupInstance2[1];
            int pceltFetched;

            e.Next(1, rgelt, out pceltFetched);
            while (pceltFetched > 0)
            {
                ISetupInstance2 raw = rgelt[0];
                insts.Add(ParseInstance(raw));
                e.Next(1, rgelt, out pceltFetched);
            }
            foreach (VSInstance inst in insts.ToArray())
            {
                foreach (string key in args)
                {
                    if (!inst.JSONBool(key))
                    {
                        insts.Remove(inst);
                    }
                }
                if (Array.IndexOf(args, "Packages") == -1)
                {
                    inst.Remove("Packages");
                }
            }
            return(insts);
        }
Esempio n. 4
0
        public static SetupInstance GetSetupInstance()
        {
            SetupInstance instance = null;

#pragma warning disable CS0219 // Variable is assigned but its value is never used
            int exHResult = 0;
#pragma warning restore CS0219 // Variable is assigned but its value is never used
            ISetupConfiguration2 configuration = SetupEnvironment.GetQuery();

            IEnumSetupInstances enumerator = configuration.EnumAllInstances();

            int fetched;
            ISetupInstance[] instances = new ISetupInstance[1];
            do
            {
                enumerator.Next(1, instances, out fetched);
                if (fetched > 0)
                {
                    instance = SetupInstance.From((ISetupInstance2)instances[0]);
                    break;
                }
            }while (fetched > 0);
            {
                exHResult = 0;
            }

            return(instance);
        }
Esempio n. 5
0
        public static void Query()
        {
            ISetupConfiguration query = new SetupConfiguration();

            ISetupConfiguration2 query2 = (ISetupConfiguration2)query;
            IEnumSetupInstances  e      = query2.EnumAllInstances();

            int pceltFetched;

            ISetupInstance2[] rgelt = new ISetupInstance2[1];
            StringBuilder     log   = new StringBuilder();

            while (true)
            {
                e.Next(1, rgelt, out pceltFetched);
                if (pceltFetched <= 0)
                {
                    Console.WriteLine(String.Format("{{\"log\":\"{0}\"}}", log.ToString()));
                    return;
                }
                if (CheckInstance(rgelt[0], ref log))
                {
                    return;
                }
            }
        }
Esempio n. 6
0
        public static bool FindVSVersion()
        {
            try
            {
                SetupConfiguration  Setup      = new SetupConfiguration();
                IEnumSetupInstances Enumerator = Setup.EnumAllInstances();

                ISetupInstance[] Instances = new ISetupInstance[1];
                for (; ;)
                {
                    int NumFetched;
                    Enumerator.Next(1, Instances, out NumFetched);

                    if (NumFetched == 0)
                    {
                        break;
                    }

                    ISetupInstance2 Instance = (ISetupInstance2)Instances[0];
                    if ((Instance.GetState() & InstanceState.Local) == InstanceState.Local)
                    {
                        string VersionString = Instance.GetDisplayName();
                        if (VersionString.Contains("19"))
                        {
                            return(true);
                        }
                    }
                }
            }
            catch
            {
            }
            return(true);
        }
Esempio n. 7
0
        /// <summary>
        /// Gets launchable instances of Visual Studio on the machine.
        /// </summary>
        /// <returns>An <see cref="IEnumerable{VisualStudioInstance}" /> of all launchable instances of Visual Studio.</returns>
        public static IEnumerable <VisualStudioInstance> GetLaunchableInstances()
        {
            int fetched = 1;

            ISetupInstance[] instances = new ISetupInstance[fetched];

            IEnumSetupInstances enumerator = SetupConfiguration.EnumInstances();

            do
            {
                enumerator.Next(fetched, instances, out fetched);

                if (fetched > 0)
                {
                    ISetupInstance2 instance;

                    try
                    {
                        instance = instances[0] as ISetupInstance2;
                    }
                    catch (COMException e) when(e.HResult == unchecked ((int)0x80070490))
                    {
                        continue;
                    }

                    if (instance != null)
                    {
                        yield return(new VisualStudioInstance(instance));
                    }
                }
            }while (fetched > 0);
        }
Esempio n. 8
0
        /// <summary>
        /// Enumerates the list of installed VS instances and returns the first one
        /// matching <see cref="MajorVersion"/>. Right now there is no way for the user to influence
        /// which version to choose if multiple installed version have the same major,
        /// e.g. VS2017 installed as Enterprise and Professional.
        /// </summary>
        /// <returns>
        /// Path to the top level directory of the Visual studio installation directory,
        /// e.g. <c>C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise</c>
        /// </returns>
        protected string GetVsInstallDir()
        {
            if (myVisualStudioInstallationPath != null)
            {
                return(myVisualStudioInstallationPath);
            }

            var setupConfiguration = (ISetupConfiguration2)GetSetupConfiguration();
            IEnumSetupInstances instancesEnumerator = setupConfiguration.EnumAllInstances();

            int fetched;
            var instances = new ISetupInstance[1];

            while (true)
            {
                instancesEnumerator.Next(1, instances, out fetched);
                if (fetched <= 0)
                {
                    break;
                }

                var instance = (ISetupInstance2)instances[0];
                if (!Version.TryParse(instance.GetInstallationVersion(), out Version version))
                {
                    Trace.TraceError("Failed to retrieve version. Skipping VS instance.");
                    continue;
                }

                if (version.Major != myMajorVersion)
                {
                    continue;
                }

                if (myVisualStudioInstallationPath != null)
                {
                    Trace.TraceWarning("Already found a Visual Studio version. Ignoring version at {0}", instance.GetInstallationPath());
                    continue;
                }

                var state = instance.GetState();
                if (state.HasFlag(InstanceState.Local) && state.HasFlag(InstanceState.Registered))
                {
                    myVisualStudioInstallationPath = instance.GetInstallationPath();
                    Trace.TraceInformation("Found matching Visual Studio version at {0}", myVisualStudioInstallationPath);
                }
                else
                {
                    Trace.TraceWarning("Ignoring incomplete Visual Studio version at {0}", instance.GetInstallationPath());
                }
            }

            return(myVisualStudioInstallationPath);
        }
        private string GetExeToolPathFromSetupConfiguration(ILogger logger)
        {
            string toolPath = null;

            logger.LogDebug(Resources.CONV_DIAG_LocatingCodeCoverageToolSetupConfiguration);
            ISetupConfiguration configurationQuery = setupConfigurationFactory.GetSetupConfigurationQuery();

            if (configurationQuery != null)
            {
                IEnumSetupInstances instanceEnumerator = configurationQuery.EnumInstances();

                int fetched;
                ISetupInstance[] tempInstance = new ISetupInstance[1];

                List <ISetupInstance2> instances = new List <ISetupInstance2>();
                //Enumerate the configuration instances
                do
                {
                    instanceEnumerator.Next(1, tempInstance, out fetched);
                    if (fetched > 0)
                    {
                        ISetupInstance2 instance = (ISetupInstance2)tempInstance[0];
                        if (instance.GetPackages().Any(p => p.GetId() == CodeCoverageInstallationPackage))
                        {
                            //Store instances that have code coverage package installed
                            instances.Add((ISetupInstance2)tempInstance[0]);
                        }
                    }
                } while (fetched > 0);

                if (instances.Count > 1)
                {
                    logger.LogDebug(Resources.CONV_DIAG_MultipleVsVersionsInstalled, string.Join(", ", instances.Select(i => i.GetInstallationVersion())));
                }

                //Get the installation path for the latest visual studio found
                var visualStudioPath = instances.OrderByDescending(i => i.GetInstallationVersion())
                                       .Select(i => i.GetInstallationPath())
                                       .FirstOrDefault();

                if (visualStudioPath != null)
                {
                    toolPath = Path.Combine(visualStudioPath, TeamToolPathandExeName);
                }
            }
            else
            {
                logger.LogDebug(Resources.CONV_DIAG_SetupConfigurationNotSupported);
            }

            return(toolPath);
        }
Esempio n. 10
0
        public static VisualStudioInstance GetVisualStudioInstance()
        {
            if (VisualStudioInstanceList == null)
            {
                VisualStudioInstanceList = new List <VisualStudioInstance>();

                try
                {
                    ISetupConfiguration2 setupConfiguration = new SetupConfigurationClass() as ISetupConfiguration2;

                    if (setupConfiguration != null)
                    {
                        IEnumSetupInstances instanceEnumerator = setupConfiguration.EnumAllInstances();
                        ISetupInstance2[]   instances          = new ISetupInstance2[3];

                        instanceEnumerator.Next(instances.Length, instances, out var instancesFetched);

                        if (instancesFetched == 0)
                        {
                            return(null);
                        }

                        do
                        {
                            for (int i = 0; i < instancesFetched; i++)
                            {
                                VisualStudioInstanceList.Add(new VisualStudioInstance(instances[i]));
                            }

                            instanceEnumerator.Next(instances.Length, instances, out instancesFetched);
                        }while (instancesFetched != 0);
                    }
                }
                catch { }
            }

            foreach (VisualStudioInstance instance in VisualStudioInstanceList)
            {
                if (
                    instance.HasRequiredDependency &&
                    instance.DisplayName.EndsWith("2019", StringComparison.OrdinalIgnoreCase) // HACK
                    )
                {
                    return(instance);
                }
            }

            return(null);
        }
Esempio n. 11
0
        private IEnumerable <VisualStudioInstance> EnumInstances(IEnumSetupInstances enumerator)
        {
            int fetched = 1;

            ISetupInstance[] instances = new ISetupInstance[fetched];

            do
            {
                enumerator.Next(fetched, instances, out fetched);

                if (fetched > 0)
                {
                    yield return(new VisualStudioInstance(instances[0] as ISetupInstance2));
                }
            }while (fetched > 0);
        }
Esempio n. 12
0
        public static VisualStudioInstance GetVisualStudioInstance()
        {
            if (VisualStudioInstanceList == null)
            {
                VisualStudioInstanceList = new List <VisualStudioInstance>();

                try
                {
                    if (new SetupConfigurationClass() is ISetupConfiguration2 setupConfiguration)
                    {
                        IEnumSetupInstances instanceEnumerator = setupConfiguration.EnumAllInstances();
                        ISetupInstance2[]   instances          = new ISetupInstance2[3];

                        instanceEnumerator.Next(instances.Length, instances, out var instancesFetched);

                        if (instancesFetched == 0)
                        {
                            return(null);
                        }

                        do
                        {
                            for (int i = 0; i < instancesFetched; i++)
                            {
                                VisualStudioInstanceList.Add(new VisualStudioInstance(instances[i]));
                            }

                            instanceEnumerator.Next(instances.Length, instances, out instancesFetched);
                        }while (instancesFetched != 0);
                    }
                }
                catch { }
            }

            foreach (VisualStudioInstance instance in VisualStudioInstanceList)
            {
                if (instance.HasRequiredDependency)
                {
                    return(instance);
                }
            }

            return(null);
        }
Esempio n. 13
0
        private static IEnumerable <ISetupInstance> GetSetupInstances()
        {
            ISetupConfiguration setupConfiguration = new SetupConfiguration();
            IEnumSetupInstances enumerator         = setupConfiguration.EnumInstances();

            int count;

            do
            {
                ISetupInstance[] setupInstances = new ISetupInstance[1];
                enumerator.Next(1, setupInstances, out count);
                if (count == 1 &&
                    setupInstances != null &&
                    setupInstances.Length == 1 &&
                    setupInstances[0] != null)
                {
                    yield return(setupInstances[0]);
                }
            }while (count == 1);
        }
Esempio n. 14
0
        private string GetVS2019Path()
        {
            try
            {
                SetupConfiguration setupConfiguration = new SetupConfiguration();

                ISetupInstance[] iSetupInstance = new ISetupInstance[1];

                IEnumSetupInstances iEnumSetupInstances = setupConfiguration.EnumAllInstances();
                while (true)
                {
                    {
                        int test;
                        iEnumSetupInstances.Next(1, iSetupInstance, out test);
                        if (test == 0)
                        {
                            break;
                        }
                    }

                    ISetupInstance2 iSetupInstance2 = (ISetupInstance2)iSetupInstance[0];
                    if ((iSetupInstance2.GetState() & InstanceState.Local) == InstanceState.Local)
                    {
                        string InstallationVersion = iSetupInstance2.GetInstallationVersion();
                        if (!string.IsNullOrEmpty(InstallationVersion))
                        {
                            string vs = InstallationVersion.Remove(InstallationVersion.IndexOf('.'));
                            if (vs == "16")
                            {
                                return(iSetupInstance2.GetInstallationPath());
                            }
                        }
                    }
                }
            }
            catch
            {
            }
            throw new Exception("Visual Studio 2019 неустановлена.");
            return(null);
        }
        private static IEnumerable <IVisualStudioInfo> DetectNewVisualStudios()
        {
            SetupConfiguration configuration;

            try
            {
                configuration = new SetupConfiguration();
            }
            catch (COMException ex)
            {
                // class not registered, no VS2017+ installations
                if ((uint)ex.HResult == 0x80040154)
                {
                    yield break;
                }

                throw;
            }
            IEnumSetupInstances e = configuration.EnumAllInstances();

            int fetched;

            ISetupInstance[] instances = new ISetupInstance[1];
            do
            {
                e.Next(1, instances, out fetched);
                if (fetched <= 0)
                {
                    continue;
                }

                ISetupInstance2 instance2 = (ISetupInstance2)instances[0];
                string          filename  = Path.Combine(instance2.GetInstallationPath(), @"Common7\IDE\devenv.exe");
                if (File.Exists(filename))
                {
                    yield return(new VisualStudio2017Info(Version.Parse(instance2.GetInstallationVersion()), instance2.GetDisplayName(), filename));
                }
            }while (fetched > 0);
        }
        /// <summary>
        /// Returns the list of <seealso cref="VisualStudioInstance"/> representing the installed
        /// Visual Studio instances on the machine.
        /// </summary>
        /// <param name="lcid">
        /// The local id to use for returning internationalized strings.
        /// </param>
        /// <param name="includeIncompleteInstances">
        /// If true, does all instances, even those that are incomplete. If false, the default,
        /// does just completed instances.
        /// </param>
        /// <param name="includeAllPackages">
        /// If true, includes all packages with the returned results. The default is to only
        /// return the main installation data.
        /// </param>
        /// <returns>
        /// The list of installed instances. An empty list if none are installed.
        /// </returns>
        public static IList <VisualStudioInstance> GetInstalledInstances(int lcid = 0,
                                                                         Boolean includeIncompleteInstances = false,
                                                                         Boolean includeAllPackages         = false)
        {
            List <VisualStudioInstance> resultList = new List <VisualStudioInstance>();

            // Grab the config interface and enumerate the instances.
            var setupConfig = GetSetupConfig();

            if (setupConfig != null)
            {
                IEnumSetupInstances instanceList = null;
                if (includeIncompleteInstances == false)
                {
                    instanceList = setupConfig.EnumInstances();
                }
                else
                {
                    instanceList = setupConfig.EnumAllInstances();
                }

                // Now it is time to loop!
                int fetched;
                ISetupInstance2[] instance = new ISetupInstance2[1];
                do
                {
                    instanceList.Next(1, instance, out fetched);
                    if (fetched > 0)
                    {
                        var filledInstance = FillInInstanceData(instance[0],
                                                                lcid,
                                                                includeAllPackages);
                        resultList.Add(filledInstance);
                    }
                } while (fetched > 0);
            }

            return(resultList);
        }
Esempio n. 17
0
        private static List <ISetupInstance> GetVisualStudioInstances()
        {
            List <ISetupInstance> vsInstances = new();

            try
            {
                SetupConfiguration   setupConfiguration  = new();
                ISetupConfiguration2 setupConfiguration2 = setupConfiguration;
                IEnumSetupInstances  setupInstances      = setupConfiguration2.EnumInstances();
                ISetupInstance[]     instances           = new ISetupInstance[1];
                int fetched = 0;

                do
                {
                    setupInstances.Next(1, instances, out fetched);

                    if (fetched > 0)
                    {
                        ISetupInstance2 instance = (ISetupInstance2)instances[0];

                        // .NET Workloads only shipped in 17.0 and later and we should only look at IDE based SKUs
                        // such as community, professional, and enterprise.
                        if (Version.TryParse(instance.GetInstallationVersion(), out Version version) &&
                            version.Major >= 17 &&
                            s_visualStudioProducts.Contains(instance.GetProduct().GetId()))
                        {
                            vsInstances.Add(instances[0]);
                        }
                    }
                }while (fetched > 0);
            }
            catch (COMException e) when(e.ErrorCode == REGDB_E_CLASSNOTREG)
            {
                // Query API not registered, good indication there are no VS installations of 15.0 or later.
                // Other exceptions are passed through since that likely points to a real error.
            }

            return(vsInstances);
        }
Esempio n. 18
0
        static List <VisualStudioInstallation> GetVisualStudioInstallations()
        {
            CachedInstalls.Clear();

            try
            {
                SetupConfiguration  Setup      = new SetupConfiguration();
                IEnumSetupInstances Enumerator = Setup.EnumAllInstances();

                ISetupInstance[] Instances = new ISetupInstance[1];
                for (; ;)
                {
                    int NumFetched;
                    Enumerator.Next(1, Instances, out NumFetched);

                    if (NumFetched == 0)
                    {
                        break;
                    }

                    ISetupInstance2 Instance = (ISetupInstance2)Instances[0];
                    if ((Instance.GetState() & InstanceState.Local) == InstanceState.Local)
                    {
                        string   VersionString = Instance.GetInstallationVersion();
                        string[] Components    = VersionString.Split('.');

                        if (Components.Length == 0)
                        {
                            continue;
                        }

                        int    MajorVersion;
                        string InstallationPath = Instance.GetInstallationPath();
                        string DevEnvPath       = Path.Combine(InstallationPath, "Common7\\IDE\\devenv.exe");

                        if (!int.TryParse(Components[0], out MajorVersion) || (MajorVersion != 15 && MajorVersion != 16))
                        {
                            continue;
                        }


                        if (!File.Exists(DevEnvPath))
                        {
                            continue;
                        }

                        VisualStudioInstallation Installation = new VisualStudioInstallation()
                        {
                            BaseDir = InstallationPath, DevEnvPath = DevEnvPath, MajorVersion = MajorVersion, ROTMoniker = string.Format("!VisualStudio.DTE.{0}.0", MajorVersion)
                        };

                        CachedInstalls.Add(Installation);
                    }
                }
            }
            catch (Exception Ex)
            {
                MessageBox.Show(string.Format("Exception while finding Visual Studio installations {0}", Ex.Message));
            }

            // prefer newer versions
            CachedInstalls.Sort((A, B) => { return(-A.MajorVersion.CompareTo(B.MajorVersion)); });

            return(CachedInstalls);
        }
Esempio n. 19
0
        public static VisualStudioInstance GetVisualStudioInstance()
        {
            if (VisualStudioInstanceList == null)
            {
                VisualStudioInstanceList = new List <VisualStudioInstance>();

                try
                {
                    if (new SetupConfigurationClass() is ISetupConfiguration2 setupConfiguration)
                    {
                        IEnumSetupInstances instanceEnumerator = setupConfiguration.EnumAllInstances();
                        ISetupInstance2[]   instances          = new ISetupInstance2[3];

                        instanceEnumerator.Next(instances.Length, instances, out var instancesFetched);

                        if (instancesFetched == 0)
                        {
                            return(null);
                        }

                        do
                        {
                            for (int i = 0; i < instancesFetched; i++)
                            {
                                VisualStudioInstanceList.Add(new VisualStudioInstance(instances[i]));
                            }

                            instanceEnumerator.Next(instances.Length, instances, out instancesFetched);
                        }while (instancesFetched != 0);
                    }
                }
                catch { }

                VisualStudioInstanceList.Sort((p1, p2) =>
                {
                    if (Version.TryParse(p1.InstallationVersion, out Version version1) && Version.TryParse(p2.InstallationVersion, out Version version2))
                    {
                        if (version1 < version2)
                        {
                            return(1);
                        }
                        else if (version1 > version2)
                        {
                            return(-1);
                        }

                        return(version1.CompareTo(version2));
                    }

                    return(1);
                });
            }

            if (VisualStudioInstance == null)
            {
                foreach (VisualStudioInstance instance in VisualStudioInstanceList)
                {
                    if (instance.HasRequiredDependency)
                    {
                        VisualStudioInstance = instance;
                        break;
                    }
                }
            }

            return(VisualStudioInstance);
        }
Esempio n. 20
0
        /// <summary>
        /// Gets the installed Visual Studio instances (the first item is the latest version).
        /// </summary>
        /// <returns>The install locations.</returns>
        public static IReadOnlyList <VisualStudioInstance> GetInstances()
        {
            if (_installDirs == null)
            {
                _installDirs = new List <VisualStudioInstance>();

                if (Environment.OSVersion.Platform == PlatformID.Win32NT)
                {
                    // Visual Studio 2017-2020
                    List <VisualStudioInstance> preReleaseInstallDirs = null;
                    try
                    {
                        SetupConfiguration  setup      = new SetupConfiguration();
                        IEnumSetupInstances enumerator = setup.EnumAllInstances();

                        ISetupInstance[] instances = new ISetupInstance[1];
                        while (true)
                        {
                            enumerator.Next(1, instances, out int fetchedCount);
                            if (fetchedCount == 0)
                            {
                                break;
                            }

                            ISetupInstance2 instance = (ISetupInstance2)instances[0];
                            if ((instance.GetState() & InstanceState.Local) == InstanceState.Local)
                            {
                                VisualStudioVersion version;
                                string displayName = instance.GetDisplayName();
                                if (displayName.Contains("2019"))
                                {
                                    version = VisualStudioVersion.VisualStudio2019;
                                }
                                else if (displayName.Contains("2017"))
                                {
                                    version = VisualStudioVersion.VisualStudio2017;
                                }
                                else
                                {
                                    throw new Exception(string.Format("Unknown Visual Studio installation. Display name: {0}", displayName));
                                }

                                var vsInstance = new VisualStudioInstance
                                {
                                    Version = version,
                                    Path    = instance.GetInstallationPath(),
                                };

                                if (instance is ISetupInstanceCatalog catalog && catalog.IsPrerelease())
                                {
                                    if (preReleaseInstallDirs == null)
                                    {
                                        preReleaseInstallDirs = new List <VisualStudioInstance>();
                                    }
                                    preReleaseInstallDirs.Add(vsInstance);
                                }
                                else
                                {
                                    _installDirs.Add(vsInstance);
                                }
                            }
                        }
                    }
Esempio n. 21
0
        /// <summary>
        /// Uses the VS SetupConfiguration COM API to resolve a path to a version-specific executable like DevEnv.exe or MSBuild.exe.
        /// </summary>
        /// <param name="buildVersionPath">Used to build a version-specific path to try to resolve.</param>
        /// <param name="minMajorVersion">The minimum major version to look for. This defaults to <see cref="VS2017MajorVersion"/>.</param>
        /// <param name="maxMajorVersion">The maximum major version to look for. This defaults to <see cref="int.MaxValue"/>.</param>
        /// <param name="resolvedPathMustExist">Whether the resolved version-specific path must exist. This defaults to false.</param>
        /// <returns>The resolved path for the highest matched version or null if no version was matched.</returns>
        public static string?ResolvePath(
            Func <Version, string> buildVersionPath,
            int minMajorVersion        = VS2017MajorVersion,
            int maxMajorVersion        = int.MaxValue,
            bool resolvedPathMustExist = false)
        {
            string? result        = null;
            Version?resultVersion = null;

            // VS 2017 and up allow multiple side-by-side editions to be installed, and we have to use a COM API to enumerate the installed instances.
            // https://github.com/mluparu/vs-setup-samples - COM API samples
            // https://code.msdn.microsoft.com/Visual-Studio-Setup-0cedd331 - More Q&A about the COM API samples
            // https://blogs.msdn.microsoft.com/vcblog/2017/03/06/finding-the-visual-c-compiler-tools-in-visual-studio-2017/#comment-273625
            // https://github.com/Microsoft/vswhere - A redistributable .exe for enumerating the VS instances from the command line.
            // https://blogs.msdn.microsoft.com/heaths/2016/09/15/changes-to-visual-studio-15-setup/
            const int REGDB_E_CLASSNOTREG = -2147221164;             // 0x80040154

            try
            {
                // From MS example: https://github.com/Microsoft/vs-setup-samples/blob/master/Setup.Configuration.CS/Program.cs
                SetupConfiguration configuration = new();

                IEnumSetupInstances instanceEnumerator = configuration.EnumAllInstances();
                int fetched;
                ISetupInstance[] instances = new ISetupInstance[1];
                do
                {
                    instanceEnumerator.Next(1, instances, out fetched);
                    if (fetched > 0)
                    {
                        ISetupInstance instance = instances[0];
                        if (instance != null &&
                            Version.TryParse(instance.GetInstallationVersion(), out Version? version) &&
                            version.Major >= minMajorVersion &&
                            version.Major <= maxMajorVersion)
                        {
                            InstanceState state = ((ISetupInstance2)instance).GetState();
                            if (state == InstanceState.Complete)
                            {
                                string?versionPath = buildVersionPath?.Invoke(version);
                                if (!string.IsNullOrEmpty(versionPath))
                                {
                                    string resolvedPath = instance.ResolvePath(versionPath);
                                    if ((resultVersion == null || resultVersion < version) &&
                                        (!resolvedPathMustExist || File.Exists(resolvedPath) || Directory.Exists(resolvedPath)))
                                    {
                                        result        = resolvedPath;
                                        resultVersion = version;

                                        // If we're looking for a single version, then we don't need to keep looking.
                                        if (minMajorVersion == maxMajorVersion)
                                        {
                                            break;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }while (fetched > 0);
            }
#pragma warning disable CC0004 // Catch block cannot be empty
            catch (COMException ex) when(ex.HResult == REGDB_E_CLASSNOTREG)
            {
                // The SetupConfiguration API is not registered, so assume no instances are installed.
            }
            catch (Exception)
            {
                // Heath Stewart (MSFT), the author of the SetupConfiguration API, says to treat any exception as "no instances installed."
                // https://code.msdn.microsoft.com/windowsdesktop/Visual-Studio-Setup-0cedd331/view/Discussions#content
            }
#pragma warning restore CC0004 // Catch block cannot be empty

            return(result);
        }