aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKarsten Heimrich <karsten.heimrich@qt.io>2022-02-14 14:59:59 +0100
committerKarsten Heimrich <karsten.heimrich@qt.io>2022-02-17 15:26:41 +0000
commitab62be90eb00433e7d40f76873b2ccdca7fb0a1e (patch)
treec18de71bd5712319b7bc32c921d5d59acdcdd72d
parent108d008f6d3ec23ee063ac0210d337ec27809665 (diff)
Refactor OutputWindowPane handling
Refactor access to the OutputWindowPane into a separate class and enable async get/write of the output window pane. Remove some of the previously introduced function calls to ThreadHelper.ThrowIfNotOnUIThread(); as they are not necessary in the VSIX loading code path. Change-Id: I2c7f5f84ec556526fe78612d981f28210c545f45 Reviewed-by: Miguel Costa <miguel.costa@qt.io>
-rw-r--r--QtVsTools.Core/Messages.cs122
-rw-r--r--QtVsTools.Core/OutputWindowPane.cs179
-rw-r--r--QtVsTools.Core/QtVsTools.Core.csproj1
3 files changed, 216 insertions, 86 deletions
diff --git a/QtVsTools.Core/Messages.cs b/QtVsTools.Core/Messages.cs
index aa39acfd..4ff66d8f 100644
--- a/QtVsTools.Core/Messages.cs
+++ b/QtVsTools.Core/Messages.cs
@@ -27,41 +27,25 @@
****************************************************************************/
using EnvDTE;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Threading;
+using QtVsTools.VisualStudio;
+using System;
using System.Collections.Concurrent;
-using System.Diagnostics;
-using System.Linq;
using System.Threading;
-using System.Windows.Forms;
using System.Threading.Tasks;
-using Microsoft.VisualStudio.Threading;
-using QtVsTools.VisualStudio;
-using Microsoft.VisualStudio.Shell;
-
-using Thread = System.Threading.Thread;
-using Task = System.Threading.Tasks.Task;
+using System.Windows.Forms;
namespace QtVsTools.Core
{
+ using Task = System.Threading.Tasks.Task;
+
public static class Messages
{
- private static OutputWindow Window { get; set; }
private static OutputWindowPane Pane { get; set; }
- private static OutputWindowPane _BuildPane;
- private static OutputWindowPane BuildPane
- {
- get
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- return _BuildPane ?? (_BuildPane = Window.OutputWindowPanes.Cast<OutputWindowPane>()
- .Where(pane =>
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- return pane.Guid == "{1BD8A850-02D1-11D1-BEE7-00A0C913D1F8}";
- })
- .FirstOrDefault());
- }
- }
+ private static readonly string PaneName = "Qt VS Tools";
+ private static readonly Guid PaneGuid = new Guid("8f6a1e44-fa0b-49e5-9934-1c050555350e");
/// <summary>
/// Show a message on the output pane.
@@ -74,21 +58,9 @@ namespace QtVsTools.Core
Text = text,
Activate = activate
});
-
- ThreadHelper.ThrowIfNotOnUIThread();
FlushMessages();
}
- static void OutputWindowPane_Print(string text)
- {
- ThreadHelper.ThrowIfNotOnUIThread();
- OutputWindowPane_Init();
- Pane.OutputString(text + "\r\n");
- // show buildPane if a build is in progress
- if (Dte.Solution.SolutionBuild.BuildState == vsBuildState.vsBuildStateInProgress)
- BuildPane?.Activate();
- }
-
/// <summary>
/// Activates the message pane of the Qt VS Tools extension.
/// </summary>
@@ -98,18 +70,13 @@ namespace QtVsTools.Core
{
Activate = true
});
-
- ThreadHelper.ThrowIfNotOnUIThread();
FlushMessages();
}
- static void OutputWindowPane_Activate()
+ static async Task OutputWindowPane_ActivateAsync()
{
- OutputWindowPane_Init();
-
- ThreadHelper.ThrowIfNotOnUIThread();
-
- Pane?.Activate();
+ await OutputWindowPane_InitAsync();
+ await Pane?.ActivateAsync();
}
private static string ExceptionToString(System.Exception e)
@@ -171,19 +138,13 @@ namespace QtVsTools.Core
{
Clear = true
});
-
- ThreadHelper.ThrowIfNotOnUIThread();
-
FlushMessages();
}
- static void OutputWindowPane_Clear()
+ static async Task OutputWindowPane_ClearAsync()
{
- OutputWindowPane_Init();
-
- ThreadHelper.ThrowIfNotOnUIThread();
-
- Pane?.Clear();
+ await OutputWindowPane_InitAsync();
+ await Pane?.ClearAsync();
}
class Msg
@@ -193,36 +154,20 @@ namespace QtVsTools.Core
public bool Activate { get; set; } = false;
}
- static bool shuttingDown = false;
static readonly ConcurrentQueue<Msg> msgQueue = new ConcurrentQueue<Msg>();
- static DTE Dte { get; set; } = null;
-
- private static void OnBeginShutdown()
- {
- shuttingDown = true;
- }
- private static void OutputWindowPane_Init()
+ private static async Task OutputWindowPane_InitAsync()
{
- if (Dte == null)
- Dte = VsServiceProvider.GetService<DTE>();
- var t = Stopwatch.StartNew();
-
- ThreadHelper.ThrowIfNotOnUIThread();
-
- while (Pane == null && t.ElapsedMilliseconds < 5000) {
- try {
- Window = Dte.Windows.Item(Constants.vsWindowKindOutput).Object as OutputWindow;
- Pane = Window?.OutputWindowPanes.Add(SR.GetString("Resources_QtVsTools"));
- } catch {
- }
+ try {
if (Pane == null)
- Thread.Yield();
+ Pane = await OutputWindowPane.CreateAsync(PaneName, PaneGuid);
+ } catch (Exception ex) {
+ System.Diagnostics.Debug.WriteLine(ex);
}
- Dte.Events.DTEEvents.OnBeginShutdown += OnBeginShutdown;
}
public static JoinableTaskFactory JoinableTaskFactory { get; set; }
+
static readonly object staticCriticalSection = new object();
static Task FlushTask { get; set; }
static EventWaitHandle MessageReady { get; set; }
@@ -234,7 +179,8 @@ namespace QtVsTools.Core
MessageReady = new EventWaitHandle(false, EventResetMode.AutoReset);
FlushTask = Task.Run(async () =>
{
- while (!shuttingDown) {
+ var package = VsServiceProvider.Instance as Package;
+ while (!package.Zombied) {
if (!await MessageReady.ToTask(3000))
continue;
while (!msgQueue.IsEmpty) {
@@ -242,18 +188,12 @@ namespace QtVsTools.Core
await Task.Yield();
continue;
}
- ////////////////////////////////////////////////////////////////////
- // Switch to main (UI) thread
- await JoinableTaskFactory.SwitchToMainThreadAsync();
if (msg.Clear)
- OutputWindowPane_Clear();
+ await OutputWindowPane_ClearAsync();
if (msg.Text != null)
- OutputWindowPane_Print(msg.Text);
+ await OutputWindowPane_PrintAsync(msg.Text);
if (msg.Activate)
- OutputWindowPane_Activate();
- ////////////////////////////////////////////////////////////////////
- // Switch to background thread
- await TaskScheduler.Default;
+ await OutputWindowPane_ActivateAsync();
}
}
});
@@ -261,5 +201,15 @@ namespace QtVsTools.Core
}
MessageReady.Set();
}
+
+ static async Task OutputWindowPane_PrintAsync(string text)
+ {
+ var active = await OutputWindowPane.GetActiveAsync();
+
+ await OutputWindowPane_InitAsync();
+ await Pane.PrintAsync(text);
+
+ (active?.ActivateAsync()).Forget();
+ }
}
}
diff --git a/QtVsTools.Core/OutputWindowPane.cs b/QtVsTools.Core/OutputWindowPane.cs
new file mode 100644
index 00000000..1cdc5c2a
--- /dev/null
+++ b/QtVsTools.Core/OutputWindowPane.cs
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** 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.Threading.Tasks;
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+using Microsoft.VisualStudio.Threading;
+using QtVsTools.VisualStudio;
+
+namespace QtVsTools.Core
+{
+ using Task = System.Threading.Tasks.Task;
+
+ public class OutputWindowPane
+ {
+ public enum VSOutputWindowPane
+ {
+ General,
+ Build,
+ Debug,
+ }
+
+ public static Task<OutputWindowPane> GetVSOutputWindowPaneAsync(VSOutputWindowPane pane)
+ {
+ switch (pane) {
+ case VSOutputWindowPane.General:
+ return GetAsync(VSConstants.OutputWindowPaneGuid.GeneralPane_guid);
+ case VSOutputWindowPane.Build:
+ return GetAsync(VSConstants.OutputWindowPaneGuid.BuildOutputPane_guid);
+ case VSOutputWindowPane.Debug:
+ return GetAsync(VSConstants.OutputWindowPaneGuid.DebugPane_guid);
+ default:
+ throw new InvalidOperationException("Unsupported Visual Studio output pane");
+ };
+ }
+
+ public static async Task<OutputWindowPane> GetAsync(Guid guid)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ try {
+ IVsOutputWindow w = null;
+ if (guid == VSConstants.OutputWindowPaneGuid.GeneralPane_guid) {
+ w = await VsServiceProvider.GetServiceAsync<SVsGeneralOutputWindowPane,
+ IVsOutputWindow>();
+ } else {
+ w = await VsServiceProvider.GetServiceAsync<SVsOutputWindow, IVsOutputWindow>();
+ }
+ ErrorHandler.ThrowOnFailure(w.GetPane(guid, out IVsOutputWindowPane pane));
+
+ return new OutputWindowPane(guid, pane);
+ } catch (Exception ex) {
+ System.Diagnostics.Debug.WriteLine(ex);
+ return null;
+ }
+ }
+
+ public static async Task<OutputWindowPane> CreateAsync(string name, Guid guid)
+ {
+ if (string.IsNullOrEmpty(name))
+ throw new ArgumentNullException($"{ nameof(name) } cannot be null");
+
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ try {
+ var w = await VsServiceProvider.GetServiceAsync<SVsOutputWindow, IVsOutputWindow>();
+
+ const int visible = 1, clear = 1;
+ ErrorHandler.ThrowOnFailure(w.CreatePane(guid, name, visible, clear));
+ ErrorHandler.ThrowOnFailure(w.GetPane(guid, out IVsOutputWindowPane pane));
+
+ return new OutputWindowPane(guid, pane);
+ } catch (Exception ex) {
+ System.Diagnostics.Debug.WriteLine(ex);
+ return null;
+ }
+ }
+
+ public static async Task<OutputWindowPane> GetActiveAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ try {
+ var w2 = await VsServiceProvider
+ .GetServiceAsync<SVsOutputWindow, IVsOutputWindow>() as IVsOutputWindow2;
+ ErrorHandler.ThrowOnFailure(w2.GetActivePaneGUID(out Guid guid));
+
+ IVsOutputWindow w = w2 as IVsOutputWindow;
+ ErrorHandler.ThrowOnFailure(w.GetPane(guid, out IVsOutputWindowPane pane));
+
+ return new OutputWindowPane(guid, pane);
+ } catch (Exception ex) {
+ System.Diagnostics.Debug.WriteLine(ex);
+ return null;
+ }
+ }
+
+ private Guid Guid { get; }
+ private IVsOutputWindowPane Pane { get; set; } = null;
+
+ private OutputWindowPane(Guid guid, IVsOutputWindowPane pane)
+ {
+ Guid = guid;
+ Pane = pane;
+ }
+
+ public async Task ActivateAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ if (Pane == null)
+ throw new InvalidOperationException($"{ nameof(Pane) } cannot be null");
+ Pane.Activate();
+ }
+
+ public async Task HideAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ if (Pane == null)
+ throw new InvalidOperationException($"{ nameof(Pane) } cannot be null");
+ Pane.Hide();
+ }
+
+ public async Task ClearAsync()
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ if (Pane == null)
+ throw new InvalidOperationException($"{ nameof(Pane) } cannot be null");
+ Pane.Clear();
+ }
+
+ public void Print()
+ {
+ ThreadHelper.JoinableTaskFactory.Run(async () => { await PrintAsync(""); });
+ }
+
+ public void Print(string value)
+ {
+ ThreadHelper.JoinableTaskFactory.Run(async () => { await PrintAsync(value); });
+ }
+
+ public Task PrintAsync()
+ {
+ return PrintAsync("");
+ }
+
+ public async Task PrintAsync(string value)
+ {
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync();
+ if (Pane is IVsOutputWindowPaneNoPump noPumpPane)
+ noPumpPane.OutputStringNoPump(value + Environment.NewLine);
+ else
+ ErrorHandler.ThrowOnFailure(Pane.OutputStringThreadSafe(value + Environment.NewLine));
+ }
+ }
+}
diff --git a/QtVsTools.Core/QtVsTools.Core.csproj b/QtVsTools.Core/QtVsTools.Core.csproj
index 9be3a2b5..9ac5f6fa 100644
--- a/QtVsTools.Core/QtVsTools.Core.csproj
+++ b/QtVsTools.Core/QtVsTools.Core.csproj
@@ -143,6 +143,7 @@
<Compile Include="MocCmdChecker.cs" />
<Compile Include="MsBuildProject.cs" />
<Compile Include="Observable.cs" />
+ <Compile Include="OutputWindowPane.cs" />
<Compile Include="ProFileContent.cs" />
<Compile Include="ProFileOption.cs" />
<Compile Include="ProjectExporter.cs" />