using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.Setup.Configuration; using System; using System.IO; using System.Runtime.InteropServices; namespace PortingAssistant.VisualStudio { /// /// Wrap the Microsoft.VisualStudio.Setup.Configuration.Interop API to query /// Visual Studio setup for instances installed on the machine. /// Code derived from sample: /// https://code.msdn.microsoft.com/Visual-Studio-Setup-0cedd331. /// https://github.com/dotnet/upgrade-assistant /// public class VisualStudioFinder : IVisualStudioFinder { private const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154); private readonly ILogger _logger; public VisualStudioFinder(ILogger logger) { _logger = logger; } public VisualStudioFinder() { } public string GetLatestVisualStudioPath() { var latest = GetLatestPath(); if (latest is null) { return null; } var version = Version.Parse(latest.GetInstallationVersion()); var installation = latest.GetInstallationPath(); if (Directory.Exists(installation)) { return installation; } else { return null; } } private static ISetupInstance2 GetLatestPath() { var result = default(ISetupInstance2); var resultVersion = new Version(0, 0); try { // This code is not obvious. See the sample (link above) for reference. var query = (ISetupConfiguration2)GetQuery(); var e = query.EnumAllInstances(); int fetched; var instances = new ISetupInstance[1]; do { // Call e.Next to query for the next instance (single item or nothing returned). e.Next(1, instances, out fetched); if (fetched <= 0) { continue; } var instance = (ISetupInstance2)instances[0]; var state = instance.GetState(); if (!Version.TryParse(instance.GetInstallationVersion(), out var version)) { continue; } // If the install was complete and a valid version, consider it. if (state == InstanceState.Complete || (state.HasFlag(InstanceState.Registered) && state.HasFlag(InstanceState.NoRebootRequired))) { var instanceHasMSBuild = false; foreach (var package in instance.GetPackages()) { if (string.Equals(package.GetId(), "Microsoft.Component.MSBuild", StringComparison.OrdinalIgnoreCase)) { instanceHasMSBuild = true; break; } } if (instanceHasMSBuild && instance != null && version > resultVersion) { result = instance; resultVersion = version; } } } while (fetched > 0); } catch (COMException) { } catch (DllNotFoundException) { // This is OK, VS "15" or greater likely not installed. } return result; } private static ISetupConfiguration GetQuery() { try { // Try to CoCreate the class object. return new SetupConfiguration(); } catch (COMException ex) when (ex.ErrorCode == REGDB_E_CLASSNOTREG) { // Try to get the class object using app-local call. var result = NativeMethods.GetSetupConfiguration(out var query, IntPtr.Zero); if (result < 0) { throw; } return query; } } } }