/**************************************************************************** ** ** Copyright (C) 2022 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt VS Tools. ** ** $QT_BEGIN_LICENSE:GPL-EXCEPT$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 as published by the Free Software ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Threading; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.VCProjectEngine; using Microsoft.Win32; namespace QtVsTools.Core { /// /// Summary description for QtVersionManager. /// public class QtVersionManager { private static QtVersionManager instance; private readonly string regVersionPath; private readonly string strVersionKey; private Hashtable versionCache; protected QtVersionManager() { strVersionKey = "Versions"; regVersionPath = Resources.registryVersionPath; } private static readonly EventWaitHandle packageInit = new EventWaitHandle(false, EventResetMode.ManualReset); private static EventWaitHandle packageInitDone = null; public static QtVersionManager The(EventWaitHandle initDone = null) { if (initDone == null) { packageInit.WaitOne(); packageInitDone.WaitOne(); } else { packageInitDone = initDone; packageInit.Set(); } if (instance == null) instance = new QtVersionManager(); return instance; } public VersionInformation GetVersionInfo(string name) { if (name == null) return null; if (name == "$(DefaultQtVersion)") name = GetDefaultVersion(); if (versionCache == null) versionCache = new Hashtable(); var vi = versionCache[name] as VersionInformation; if (vi == null) { var qtdir = GetInstallPath(name); versionCache[name] = vi = VersionInformation.Get(qtdir); if (vi != null) vi.name = name; } return vi; } public VersionInformation GetVersionInfo(EnvDTE.Project project) { ThreadHelper.ThrowIfNotOnUIThread(); return GetVersionInfo(GetProjectQtVersion(project)); } public string[] GetVersions() { return GetVersions(Registry.CurrentUser); } public string GetQtVersionFromInstallDir(string qtDir) { if (qtDir == null) return null; var versions = GetVersions(); foreach (var version in versions) { var installPath = GetInstallPath(version); if (installPath == null) continue; if (installPath.Equals(qtDir, StringComparison.OrdinalIgnoreCase)) return version; } return null; } public string[] GetVersions(RegistryKey root) { var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false); if (key == null) return new string[] { }; var versionKey = key.OpenSubKey(strVersionKey, false); if (versionKey == null) return new string[] { }; return versionKey.GetSubKeyNames(); } /// /// Check if all Qt versions are valid and readable. /// /// /// true, if there are one or more invalid Qt version public bool HasInvalidVersions(out string errorMessage, out bool defaultVersionInvalid) { var defaultVersion = GetDefaultVersionString(); defaultVersionInvalid = string.IsNullOrEmpty(defaultVersion); errorMessage = null; foreach (var version in GetVersions()) { if (version == "$(DefaultQtVersion)") continue; var path = GetInstallPath(version); if (path != null && (path.StartsWith("SSH:") || path.StartsWith("WSL:"))) continue; if (string.IsNullOrEmpty(path) || !QMake.Exists(path)) { errorMessage += version + " in " + path + "\n"; defaultVersionInvalid |= version == defaultVersion; } if (!string.IsNullOrEmpty(errorMessage)) { errorMessage = "These Qt version are inaccessible:\n" + errorMessage + "Make sure that you have read access to all files in your Qt directories."; } } return errorMessage != null; } public void SetLatestQtVersionAsDefault() { var validVersions = new Dictionary(); foreach (var version in GetVersions()) { if (version == "$(DefaultQtVersion)") continue; var path = GetInstallPath(version); if (!string.IsNullOrEmpty(path) && QMake.Exists(path)) validVersions[version] = new Version(new QtConfig(path).VersionString); } if (validVersions.Count <= 0) return; var defaultName = ""; Version defaultVersion = null; foreach (var tmp in validVersions) { var version = tmp.Value; if (defaultVersion == null || defaultVersion < version) { defaultName = tmp.Key; defaultVersion = version; } } SaveDefaultVersion(defaultName); } public string GetInstallPath(string version) { if (version == "$(DefaultQtVersion)") version = GetDefaultVersion(); return GetInstallPath(version, Registry.CurrentUser); } public string GetInstallPath(string version, RegistryKey root) { if (version == "$(DefaultQtVersion)") version = GetDefaultVersion(root); if (version == "$(QTDIR)") return Environment.GetEnvironmentVariable("QTDIR"); var key = root.OpenSubKey("SOFTWARE\\" + Resources.registryRootPath, false); var versionKey = key?.OpenSubKey(strVersionKey + "\\" + version, false); return versionKey?.GetValue("InstallDir") as string; } public string GetInstallPath(EnvDTE.Project project) { ThreadHelper.ThrowIfNotOnUIThread(); var version = GetProjectQtVersion(project); if (version == "$(DefaultQtVersion)") version = GetDefaultVersion(); if (version == null) return null; return GetInstallPath(version); } public bool SaveVersion(string versionName, string path, bool checkPath = true) { var verName = versionName?.Trim().Replace(@"\", "_"); if (string.IsNullOrEmpty(verName)) return false; var dir = string.Empty; if (verName != "$(QTDIR)") { DirectoryInfo di; try { di = new DirectoryInfo(path); } catch { di = null; } if (di?.Exists == true) { dir = di.FullName; } else if (!checkPath) { dir = path; } else { return false; } } string rootKeyPath = "SOFTWARE\\" + Resources.registryRootPath; string versionKeyPath = strVersionKey + "\\" + verName; using (var key = Registry.CurrentUser.CreateSubKey(rootKeyPath)) { if (key == null) { Messages.Print( "ERROR: root registry key creation failed"); return false; } using (var versionKey = key.CreateSubKey(versionKeyPath)) { if (versionKey == null) { Messages.Print( "ERROR: version registry key creation failed"); return false; } versionKey.SetValue("InstallDir", dir); } } return true; } public void RemoveVersion(string versionName) { var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + regVersionPath, true); if (key == null) return; key.DeleteSubKey(versionName); key.Close(); } internal bool IsVersionAvailable(string version) { var versionAvailable = false; var versions = GetVersions(); foreach (var ver in versions) { if (version == ver) { versionAvailable = true; break; } } return versionAvailable; } public bool SaveProjectQtVersion(EnvDTE.Project project, string version) { ThreadHelper.ThrowIfNotOnUIThread(); return SaveProjectQtVersion(project, version, project.ConfigurationManager.ActiveConfiguration.PlatformName); } public bool SaveProjectQtVersion(EnvDTE.Project project, string version, string platform) { ThreadHelper.ThrowIfNotOnUIThread(); if (!IsVersionAvailable(version) && version != "$(DefaultQtVersion)") return false; if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) { var vcPro = project.Object as VCProject; if (vcPro == null) return false; foreach (VCConfiguration3 config in (IVCCollection)vcPro.Configurations) { config.SetPropertyValue(Resources.projLabelQtSettings, true, "QtInstall", version); } return true; } var key = "Qt5Version " + platform; if (!project.Globals.get_VariableExists(key) || project.Globals[key].ToString() != version) project.Globals[key] = version; if (!project.Globals.get_VariablePersists(key)) project.Globals.set_VariablePersists(key, true); return true; } public string GetProjectQtVersion(EnvDTE.Project project) { ThreadHelper.ThrowIfNotOnUIThread(); EnvDTE.Configuration config = null; try { config = project.ConfigurationManager.ActiveConfiguration; } catch { // Accessing the ActiveConfiguration property throws an exception // if there's an "unconfigured" platform in the Solution platform combo box. config = project.ConfigurationManager.Item(1); } var version = GetProjectQtVersion(project, config); if (version == null && project.Globals.get_VariablePersists("Qt5Version")) { version = (string)project.Globals["Qt5Version"]; ExpandEnvironmentVariablesInQtVersion(ref version); return VerifyIfQtVersionExists(version) ? version : null; } if (version == null) version = Legacy.QtVersionManager.GetSolutionQtVersion(project.DTE.Solution); return version; } public string GetProjectQtVersion(EnvDTE.Project project, EnvDTE.Configuration config) { ThreadHelper.ThrowIfNotOnUIThread(); if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) return QtProject.GetPropertyValue(project, config, "QtInstall"); var key = "Qt5Version " + config.PlatformName; if (!project.Globals.get_VariablePersists(key)) return null; var version = (string)project.Globals[key]; ExpandEnvironmentVariablesInQtVersion(ref version); return VerifyIfQtVersionExists(version) ? version : null; } public string GetProjectQtVersion(EnvDTE.Project project, string platform) { ThreadHelper.ThrowIfNotOnUIThread(); if (QtProject.GetFormatVersion(project) >= Resources.qtMinFormatVersion_Settings) return GetProjectQtVersion(project); var key = "Qt5Version " + platform; if (!project.Globals.get_VariablePersists(key)) return null; var version = (string)project.Globals[key]; ExpandEnvironmentVariablesInQtVersion(ref version); return VerifyIfQtVersionExists(version) ? version : null; } private static void ExpandEnvironmentVariablesInQtVersion(ref string version) { if (version != "$(QTDIR)" && version != "$(DefaultQtVersion)") { // Make it possible to specify the version name // via an environment variable var regExp = new System.Text.RegularExpressions.Regex("\\$\\((?\\S+)\\)"); var match = regExp.Match(version); if (match.Success) { var env = match.Groups["VarName"].Value; version = Environment.GetEnvironmentVariable(env); } } } public string GetDefaultVersion() { return GetDefaultVersion(Registry.CurrentUser); } public string GetDefaultVersion(RegistryKey root) { string defaultVersion = null; try { var key = root.OpenSubKey("SOFTWARE\\" + regVersionPath, false); if (key != null) defaultVersion = (string)key.GetValue("DefaultQtVersion"); } catch { Messages.DisplayWarningMessage(SR.GetString("QtVersionManager_CannotLoadQtVersion")); } if (defaultVersion == null) { MergeVersions(); var key = root.OpenSubKey("SOFTWARE\\" + regVersionPath, false); if (key != null) { var versions = GetVersions(); if (versions != null && versions.Length > 0) defaultVersion = versions[versions.Length - 1]; if (defaultVersion != null) SaveDefaultVersion(defaultVersion); } if (defaultVersion == null) { // last fallback... try QTDIR var qtDir = Environment.GetEnvironmentVariable("QTDIR"); if (qtDir == null) return null; var d = new DirectoryInfo(qtDir); SaveVersion(d.Name, d.FullName); if (SaveDefaultVersion(d.Name)) defaultVersion = d.Name; } } return VerifyIfQtVersionExists(defaultVersion) ? defaultVersion : null; } public string GetDefaultVersionString() { string defaultVersion = null; try { var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\" + regVersionPath, false); if (key != null) defaultVersion = key.GetValue("DefaultQtVersion") as string; } catch { Messages.Print("Cannot read the default Qt version from registry."); } if (defaultVersion == null) { var qtDir = Environment.GetEnvironmentVariable("QTDIR"); if (string.IsNullOrEmpty(qtDir)) return defaultVersion; } return defaultVersion; } public bool SaveDefaultVersion(string version) { if (version == "$(DefaultQtVersion)") return false; var key = Registry.CurrentUser.CreateSubKey("SOFTWARE\\" + regVersionPath); if (key == null) return false; key.SetValue("DefaultQtVersion", version); return true; } private void MergeVersions() { var hkcuVersions = GetVersions(); var hklmVersions = GetVersions(Registry.LocalMachine); var hkcuInstDirs = new string[hkcuVersions.Length]; for (var i = 0; i < hkcuVersions.Length; ++i) hkcuInstDirs[i] = GetInstallPath(hkcuVersions[i]); var hklmInstDirs = new string[hklmVersions.Length]; for (var i = 0; i < hklmVersions.Length; ++i) hklmInstDirs[i] = GetInstallPath(hklmVersions[i], Registry.LocalMachine); for (var i = 0; i < hklmVersions.Length; ++i) { if (hklmInstDirs[i] == null) continue; var found = false; for (var j = 0; j < hkcuInstDirs.Length; ++j) { if (hkcuInstDirs[j] != null && hkcuInstDirs[j].ToLower() == hklmInstDirs[i].ToLower()) { found = true; break; } } if (!found) { for (var j = 0; j < hkcuVersions.Length; ++j) { if (hkcuVersions[j] != null && hkcuVersions[j] == hklmVersions[i]) { found = true; break; } } if (!found) SaveVersion(hklmVersions[i], hklmInstDirs[i]); } } } internal bool VerifyIfQtVersionExists(string version) { if (version == "$(DefaultQtVersion)") version = GetDefaultVersion(); if (!string.IsNullOrEmpty(version)) { var regExp = new System.Text.RegularExpressions.Regex("\\$\\(.*\\)"); if (regExp.IsMatch(version)) return true; return Directory.Exists(GetInstallPath(version)); } return false; } } }