/**************************************************************************** ** ** Copyright (C) 2016 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 EnvDTE; using Microsoft.VisualStudio.Settings; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Shell.Settings; using Microsoft.VisualStudio.Threading; using QtProjectLib; using System; using System.Diagnostics; using System.IO; using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; using Task = System.Threading.Tasks.Task; namespace QtVsTools { using VisualStudio; #if VS2013 using AsyncPackage = Package; #endif [Guid(PackageGuid)] [InstalledProductRegistration("#110", "#112", Version.PRODUCT_VERSION, IconResourceID = 400)] [ProvideMenuResource("Menus.ctmenu", 1)] #if VS2013 [PackageRegistration(UseManagedResourcesOnly = true)] [ProvideAutoLoad(UIContextGuids.SolutionExists)] [ProvideAutoLoad(UIContextGuids.NoSolution)] #else [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] [ProvideAutoLoad(UIContextGuids.SolutionExists, PackageAutoLoadFlags.BackgroundLoad)] [ProvideAutoLoad(UIContextGuids.NoSolution, PackageAutoLoadFlags.BackgroundLoad)] #endif public sealed class Vsix : AsyncPackage, IVsServiceProvider { /// /// The package GUID string. /// public const string PackageGuid = "15021976-647e-4876-9040-2507afde45d2"; /// /// Gets the Visual Studio application object that hosts the package. /// public DTE Dte { get; private set; } /// /// Gets the installation path of the package. /// public string PkgInstallPath { get; private set; } static EventWaitHandle initDone = new EventWaitHandle(false, EventResetMode.ManualReset); static Vsix instance = null; /// /// Gets the instance of the package. /// public static Vsix Instance { get { initDone.WaitOne(); return instance; } } private string appWrapperPath; public string AppWrapperPath { get { if (appWrapperPath == null) appWrapperPath = locateHelperExecutable("QtAppWrapper.exe"); return appWrapperPath; } } private string qmakeFileReaderPath; public string QMakeFileReaderPath { get { if (qmakeFileReaderPath == null) qmakeFileReaderPath = locateHelperExecutable("QMakeFileReader.exe"); return qmakeFileReaderPath; } } static readonly Stopwatch initTimer = Stopwatch.StartNew(); /// /// Initialization of the package; this method is called right after the package is sited, /// so this is the place where you can put all the initialization code that rely on services /// provided by VisualStudio. /// #if VS2013 protected override void Initialize() #else protected override async Task InitializeAsync( CancellationToken cancellationToken, IProgress progress) #endif { try { var timeInitBegin = initTimer.Elapsed; VsServiceProvider.Instance = instance = this; #if !VS2013 /////////////////////////////////////////////////////////////////////////////////// // Switch to main (UI) thread await JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); var timeUiThreadBegin = initTimer.Elapsed; #endif if ((Dte = VsServiceProvider.GetService()) == null) throw new Exception("Unable to get service: DTE"); VsShellSettings.Manager = new ShellSettingsManager(this as IServiceProvider); eventHandler = new DteEventsHandler(Dte); DefaultEditorsClient.Initialize(eventHandler); DefaultEditorsClient.Instance.Listen(); Qml.Debug.Launcher.Initialize(); QtMainMenu.Initialize(this); QtSolutionContextMenu.Initialize(this); QtProjectContextMenu.Initialize(this); QtItemContextMenu.Initialize(this); DefaultEditorsHandler.Initialize(Dte); QtHelpMenu.Initialize(this); if (!string.IsNullOrEmpty(VsShell.InstallRootDir)) HelperFunctions.VCPath = Path.Combine(VsShell.InstallRootDir, "VC"); #if !VS2013 /////////////////////////////////////////////////////////////////////////////////// // Switch to background thread await TaskScheduler.Default; var timeUiThreadEnd = initTimer.Elapsed; #endif var vm = QtVersionManager.The(initDone); var error = string.Empty; if (vm.HasInvalidVersions(out error)) Messages.PaneMessageSafe(Dte, error, 5000); // determine the package installation directory var uri = new Uri(System.Reflection.Assembly .GetExecutingAssembly().EscapedCodeBase); PkgInstallPath = Path.GetDirectoryName( Uri.UnescapeDataString(uri.AbsolutePath)) + @"\"; var QtMsBuildDefault = Path.Combine( Environment.GetEnvironmentVariable("LocalAppData"), "QtMsBuild"); try { if (!Directory.Exists(QtMsBuildDefault)) Directory.CreateDirectory(QtMsBuildDefault); var qtMsBuildFiles = Directory.GetFiles( Path.Combine(PkgInstallPath, "QtMsBuild")); foreach (var qtMsBuildFile in qtMsBuildFiles) { File.Copy(qtMsBuildFile, Path.Combine( QtMsBuildDefault, Path.GetFileName(qtMsBuildFile)), true); } } catch { QtMsBuildDefault = Path.Combine(PkgInstallPath, "QtMsBuild"); } var QtMsBuildPath = Environment.GetEnvironmentVariable("QtMsBuild"); if (string.IsNullOrEmpty(QtMsBuildPath)) { Environment.SetEnvironmentVariable( "QtMsBuild", QtMsBuildDefault, EnvironmentVariableTarget.User); Environment.SetEnvironmentVariable( "QtMsBuild", QtMsBuildDefault, EnvironmentVariableTarget.Process); } CopyTextMateLanguageFiles(); UpdateDefaultEditors(Mode.Startup); CopyNatvisFile(); var modules = QtModules.Instance.GetAvailableModuleInformation(); foreach (var module in modules) { if (!string.IsNullOrEmpty(module.ResourceName)) { var translatedName = SR.GetString(module.ResourceName, this); if (!string.IsNullOrEmpty(translatedName)) module.Name = translatedName; } } Messages.PaneMessageSafe(Dte, string.Format("\r\n" + "== Qt Visual Studio Tools version {0}\r\n" + "\r\n" + " Initialized in: {1:0.##} msecs\r\n" #if !VS2013 + " Main (UI) thread: {2:0.##} msecs\r\n" #endif , Version.PRODUCT_VERSION , (initTimer.Elapsed - timeInitBegin).TotalMilliseconds #if !VS2013 , (timeUiThreadEnd - timeUiThreadBegin).TotalMilliseconds #endif ), 5000); } catch (Exception e) { Messages.PaneMessageSafe(Dte, e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace, 5000); } finally { initDone.Set(); initTimer.Stop(); } } /// /// Called to ask the package if the shell can be closed. /// /// Returns true if the shell can be closed, otherwise false. /// /// Microsoft.VisualStudio.VSConstants.S_OK if the method succeeded, otherwise an error code. /// protected override int QueryClose(out bool canClose) { if (eventHandler != null) { eventHandler.Disconnect(); DefaultEditorsClient.Instance.Shutdown(); } try { UpdateDefaultEditors(Mode.Shutdown); } catch (Exception e) { MessageBox.Show(e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace); } return base.QueryClose(out canClose); } private enum Mode { Startup = 0, Shutdown } private DteEventsHandler eventHandler; private string locateHelperExecutable(string exeName) { if (!string.IsNullOrEmpty(PkgInstallPath) && File.Exists(PkgInstallPath + exeName)) return PkgInstallPath + exeName; return null; } /// /// This is to support VS2013, both VSIX and Qt4 or Qt5 Add-In installed. Default editor /// values in the registry are changed so that Qt4 or Qt5 Add-in values are default and /// Qt5 VSIX values are set only when the VSIX is loaded. On startup, Qt5 related registry /// values for *.qrc, *.ts and *.ui extensions are written, while on shutdown possible /// existing Add-in values are written back. /// private void UpdateDefaultEditors(Mode mode) { if (mode == Mode.Shutdown) { var qt5 = new Qt5DefaultEditors(); qt5.WriteAddinRegistryValues(); var qt4 = new Qt4DefaultEditors(); qt4.WriteAddinRegistryValues(); } else { var vsix = new QtVsToolsDefaultEditors(); vsix.WriteVsixRegistryValues(this); } } private void CopyTextMateLanguageFiles() { #if (!VS2013) var settingsManager = VsShellSettings.Manager; var store = settingsManager.GetReadOnlySettingsStore(SettingsScope.UserSettings); var qttmlanguage = Environment. ExpandEnvironmentVariables("%USERPROFILE%\\.vs\\Extensions\\qttmlanguage"); if (store.GetBoolean(Statics.QmlTextMatePath, Statics.QmlTextMateKey, true)) { HelperFunctions.CopyDirectory(Path.Combine(PkgInstallPath, "qttmlanguage"), qttmlanguage); } else { Directory.Delete(qttmlanguage, true); } //Remove textmate-based QML syntax highlighting var qmlTextmate = Path.Combine(qttmlanguage, "qml"); if (Directory.Exists(qmlTextmate)) { try { Directory.Delete(qmlTextmate, true); } catch { } } #endif } public void CopyNatvisFile(string qtNamespace = null) { try { string natvis = File.ReadAllText( Path.Combine(PkgInstallPath, "qt5.natvis.xml")); string natvisFile; if (string.IsNullOrEmpty(qtNamespace)) { natvis = natvis.Replace("##NAMESPACE##::", string.Empty); natvisFile = "qt5.natvis"; } else { natvis = natvis.Replace("##NAMESPACE##", qtNamespace); natvisFile = string.Format("qt5_{0}.natvis", qtNamespace.Replace("::", "_")); } File.WriteAllText(Path.Combine( Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), #if VS2019 @"Visual Studio 2019\Visualizers\", #elif VS2017 @"Visual Studio 2017\Visualizers\", #elif VS2015 @"Visual Studio 2015\Visualizers\", #elif VS2013 @"Visual Studio 2013\Visualizers\", #endif natvisFile), natvis, System.Text.Encoding.UTF8); } catch (Exception e) { Messages.PaneMessageSafe(Dte, e.Message + "\r\n\r\nStacktrace:\r\n" + e.StackTrace, 5000); } } public I GetService() where T : class where I : class { return GetService(typeof(T)) as I; } #if !VS2013 public async Task GetServiceAsync() where T : class where I : class { return await GetServiceAsync(typeof(T)) as I; } #endif } }