diff --git a/VSIX/RapidXamlToolkit/Analysis/ErrorList/TableDataSource.cs b/VSIX/RapidXamlToolkit/Analysis/ErrorList/TableDataSource.cs index 18c3e0d9..7e6dfeab 100644 --- a/VSIX/RapidXamlToolkit/Analysis/ErrorList/TableDataSource.cs +++ b/VSIX/RapidXamlToolkit/Analysis/ErrorList/TableDataSource.cs @@ -23,6 +23,8 @@ public class TableDataSource : ITableDataSource private TableDataSource() { #if VSIXNOTEXE + ThreadHelper.ThrowIfNotOnUIThread(); + var compositionService = ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)) as IComponentModel; compositionService?.DefaultCompositionService.SatisfyImportsOnce(this); diff --git a/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlRunningDocTableEvents.cs b/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlRunningDocTableEvents.cs index 755cd363..96e6def2 100644 --- a/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlRunningDocTableEvents.cs +++ b/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlRunningDocTableEvents.cs @@ -33,6 +33,8 @@ public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint d public int OnAfterSave(uint docCookie) { #if VSIXNOTEXE + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var documentInfo = this.runningDocumentTable.GetDocumentInfo(docCookie); var documentPath = documentInfo.Moniker; diff --git a/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlTagger.cs b/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlTagger.cs index 4be83056..62abb6a0 100644 --- a/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlTagger.cs +++ b/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/RapidXamlTagger.cs @@ -101,6 +101,8 @@ private void OnXamlDocParsed(object sender, RapidXamlParsingEventArgs e) private string GetProjectName(string fileName) { #if VSIXNOTEXE + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + try { return ProjectHelpers.Dte2.Solution.FindProjectItem(fileName)?.ContainingProject?.Name ?? string.Empty; diff --git a/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/SuggestedActionsSource.cs b/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/SuggestedActionsSource.cs index b0b73f86..ed122c2a 100644 --- a/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/SuggestedActionsSource.cs +++ b/VSIX/RapidXamlToolkit/Analysis/XamlAnalysis/SuggestedActionsSource.cs @@ -33,6 +33,8 @@ public SuggestedActionsSource(IViewTagAggregatorFactoryService tagService, ISugg this.view = view; this.file = file; + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + RapidXamlDocumentCache.Add(this.file, textBuffer.CurrentSnapshot); } diff --git a/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornment.cs b/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornment.cs index 9643627e..2cf30b43 100644 --- a/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornment.cs +++ b/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornment.cs @@ -22,6 +22,8 @@ internal sealed class SymbolIconAdornment : TextBlock public SymbolIconAdornment(SymbolIconTag tag) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + this.Foreground = TextColor; this.SymbolTag = tag; this.Height = this.GetFontSize() + 2; diff --git a/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornmentTagger.cs b/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornmentTagger.cs index 702d8534..0a63a20f 100644 --- a/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornmentTagger.cs +++ b/VSIX/RapidXamlToolkit/EditorExtras/SymbolVisualizer/SymbolIconAdornmentTagger.cs @@ -76,6 +76,8 @@ protected override SymbolIconAdornment CreateAdornment(SymbolIconTag dataTag, Sn protected override bool UpdateAdornment(SymbolIconAdornment adornment, SymbolIconTag dataTag) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + adornment.Update(dataTag); return true; } diff --git a/VSIX/RapidXamlToolkit/Generation/Commands/GetXamlFromCodeWindowBaseCommand.cs b/VSIX/RapidXamlToolkit/Generation/Commands/GetXamlFromCodeWindowBaseCommand.cs index 36c2e217..71b917a0 100644 --- a/VSIX/RapidXamlToolkit/Generation/Commands/GetXamlFromCodeWindowBaseCommand.cs +++ b/VSIX/RapidXamlToolkit/Generation/Commands/GetXamlFromCodeWindowBaseCommand.cs @@ -28,6 +28,8 @@ public async Task GetXamlAsync(IAsyncServiceProvider serviceProvid if (CodeParserBase.GetSettings().Profiles.Any()) { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + if (!(await serviceProvider.GetServiceAsync(typeof(EnvDTE.DTE)) is DTE dte)) { RapidXamlPackage.Logger?.RecordError(StringRes.Error_FailedToGetDteInGetXamlAsync); @@ -96,6 +98,8 @@ public async Task GetXamlAsync(IAsyncServiceProvider serviceProvid protected static async Task ShowStatusBarMessageAsync(IAsyncServiceProvider serviceProvider, string message) { + await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(); + try { if (await serviceProvider.GetServiceAsync(typeof(EnvDTE.DTE)) is DTE dte) diff --git a/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandler.cs b/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandler.cs index 7e11516e..0bfd2f1c 100644 --- a/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandler.cs +++ b/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandler.cs @@ -133,6 +133,8 @@ private static IEnumerable EnumerateDroppedFiles(MemoryStream stream) private string GetFilename(DragDropInfo info) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (info.Data.GetData("CF_VSSTGPROJECTITEMS") is MemoryStream stream) { var tryCount = 0; diff --git a/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandlerProvider.cs b/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandlerProvider.cs index ab718a54..87190c54 100644 --- a/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandlerProvider.cs +++ b/VSIX/RapidXamlToolkit/Generation/DragDrop/RapidXamlDropHandlerProvider.cs @@ -49,6 +49,8 @@ public static async Task InitializeAsync(AsyncPackage package, ILogger logger) public IDropHandler GetAssociatedDropHandler(IWpfTextView view) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + try { ITextBufferUndoManager undoManager = this.UndoProvider.GetTextBufferUndoManager(view.TextBuffer); diff --git a/VSIX/RapidXamlToolkit/Generation/Options/ConfiguredSettings.cs b/VSIX/RapidXamlToolkit/Generation/Options/ConfiguredSettings.cs index d87a50fd..8d0c2b88 100644 --- a/VSIX/RapidXamlToolkit/Generation/Options/ConfiguredSettings.cs +++ b/VSIX/RapidXamlToolkit/Generation/Options/ConfiguredSettings.cs @@ -25,6 +25,8 @@ public ConfiguredSettings(IServiceProvider vsServiceProvider) { try { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var shellSettingsManager = new ShellSettingsManager(vsServiceProvider); this.store = shellSettingsManager.GetWritableSettingsStore(SettingsScope.UserSettings); diff --git a/VSIX/RapidXamlToolkit/Logging/RxtLogger.cs b/VSIX/RapidXamlToolkit/Logging/RxtLogger.cs index a0a79ec6..b200fa43 100644 --- a/VSIX/RapidXamlToolkit/Logging/RxtLogger.cs +++ b/VSIX/RapidXamlToolkit/Logging/RxtLogger.cs @@ -38,6 +38,8 @@ public void RecordError(string message, Dictionary properties = public void RecordGeneralError(string message) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + GeneralOutputPane.Instance.Write($"[{StringRes.RapidXamlToolkit}] {message}"); GeneralOutputPane.Instance.Activate(); } diff --git a/VSIX/RapidXamlToolkit/Logging/RxtOutputPane.cs b/VSIX/RapidXamlToolkit/Logging/RxtOutputPane.cs index 5e90bafb..5412ccd5 100644 --- a/VSIX/RapidXamlToolkit/Logging/RxtOutputPane.cs +++ b/VSIX/RapidXamlToolkit/Logging/RxtOutputPane.cs @@ -36,6 +36,8 @@ private RxtOutputPane() public static bool IsInitialized() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (ServiceProvider.GlobalProvider.GetService(typeof(SVsOutputWindow)) is IVsOutputWindow outWindow) { outWindow.GetPane(ref rxtPaneGuid, out IVsOutputWindowPane pane); diff --git a/VSIX/RapidXamlToolkit/ProjectHelpers.cs b/VSIX/RapidXamlToolkit/ProjectHelpers.cs index a5878880..1daf5eb5 100644 --- a/VSIX/RapidXamlToolkit/ProjectHelpers.cs +++ b/VSIX/RapidXamlToolkit/ProjectHelpers.cs @@ -4,7 +4,6 @@ using EnvDTE; using EnvDTE80; using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Text.Operations; namespace RapidXamlToolkit { @@ -12,6 +11,8 @@ public static class ProjectHelpers { static ProjectHelpers() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + Dte = (DTE)Package.GetGlobalService(typeof(DTE)); Dte2 = (DTE2)Package.GetGlobalService(typeof(DTE)); } diff --git a/VSIX/RapidXamlToolkit/VisualStudioIntegration/SolutionExtensions.cs b/VSIX/RapidXamlToolkit/VisualStudioIntegration/SolutionExtensions.cs index a255eacc..cd267917 100644 --- a/VSIX/RapidXamlToolkit/VisualStudioIntegration/SolutionExtensions.cs +++ b/VSIX/RapidXamlToolkit/VisualStudioIntegration/SolutionExtensions.cs @@ -12,6 +12,8 @@ public static class SolutionExtensions public static IEnumerable GetAllProjects(this EnvDTE.Solution solution) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var item = solution.Projects.GetEnumerator(); while (item.MoveNext()) @@ -37,6 +39,8 @@ public static class SolutionExtensions public static IEnumerable GetSolutionFolderProjects(this EnvDTE.Project solutionFolder) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + for (var i = 1; i <= solutionFolder.ProjectItems.Count; i++) { var subProject = solutionFolder.ProjectItems.Item(i).SubProject; @@ -62,6 +66,8 @@ public static class SolutionExtensions public static EnvDTE.Project GetProjectContainingFile(this EnvDTE.Solution solution, string filePath) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + return solution.FindProjectItem(filePath).ContainingProject; } } diff --git a/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioAbstraction.cs b/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioAbstraction.cs index 3f3132c5..f9966092 100644 --- a/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioAbstraction.cs +++ b/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioAbstraction.cs @@ -42,11 +42,15 @@ public VisualStudioAbstraction(ILogger logger, IAsyncServiceProvider serviceProv public string GetActiveDocumentFilePath() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + return this.Dte.ActiveDocument.FullName; } public ProjectType GetProjectType(EnvDTE.Project project) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + const string WpfGuid = "{60DC8134-EBA5-43B8-BCC9-BB4BC16C2548}"; const string UwpGuid = "{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A}"; const string XamAndroidGuid = "{EFBA0AD7-5A72-4C68-AF49-83D382785DCF}"; @@ -253,6 +257,8 @@ ProjectType GetProjectTypeOfReferencingProject(EnvDTE.Project proj) public bool ProjectUsesWpf(EnvDTE.Project project) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var rawContent = System.IO.File.ReadAllText(project.FullName); return this.ProjectUsesWpf(rawContent); @@ -285,6 +291,8 @@ public string GetProjectTypeGuids(EnvDTE.Project proj) { string projectTypeGuids = string.Empty; + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + try { object service = this.GetService(proj.DTE, typeof(IVsSolution)); @@ -309,6 +317,8 @@ public string GetProjectTypeGuids(EnvDTE.Project proj) public Guid GetProjectGuid(EnvDTE.Project proj) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var projectGuid = Guid.Empty; try @@ -339,6 +349,8 @@ public object GetService(object serviceProvider, Type type) public object GetService(object serviceProviderObject, Guid guid) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + object service = null; Microsoft.VisualStudio.OLE.Interop.IServiceProvider provider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)serviceProviderObject; @@ -361,6 +373,8 @@ public object GetService(object serviceProviderObject, Guid guid) public string GetActiveDocumentText() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var activeDoc = this.Dte.ActiveDocument; if (activeDoc.Object("TextDocument") is EnvDTE.TextDocument objectDoc) @@ -414,6 +428,8 @@ public bool UserConfirms(string title, string message) public (int Position, int LineNo) GetCursorPositionAndLineNumber() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var offset = ((TextSelection)this.Dte.ActiveDocument.Selection).AnchorPoint.AbsoluteCharOffset; var lineNo = ((TextSelection)this.Dte.ActiveDocument.Selection).CurrentLine; @@ -452,17 +468,23 @@ public async Task GetXamlIndentAsync() public string GetPathOfProjectContainingFile(string fileName) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + return this.Dte.Solution?.GetProjectContainingFile(fileName)?.FileName; } public (string ProjectFileName, ProjectType ProjectType) GetNameAndTypeOfProjectContainingFile(string fileName) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var proj = this.Dte.Solution.GetProjectContainingFile(fileName); return (proj.FileName, this.GetProjectType(proj)); } public string GetLanguageFromContainingProject(string fileName) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + try { var proj = ProjectHelpers.Dte.Solution.GetProjectContainingFile(fileName); @@ -498,6 +520,8 @@ public string GetLanguageFromContainingProject(string fileName) public List GetFilesFromContainingProject(string fileName, params string[] fileNameEndings) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + var result = new List(); // See also https://docs.microsoft.com/en-us/dotnet/api/microsoft.visualstudio.shell.interop.ivssolution.getprojectenum?view=visualstudiosdk-2017#Microsoft_VisualStudio_Shell_Interop_IVsSolution_GetProjectEnum_System_UInt32_System_Guid__Microsoft_VisualStudio_Shell_Interop_IEnumHierarchies__ @@ -549,6 +573,8 @@ void IterateProject(EnvDTE.Project project) private NuGet.VisualStudio.Contracts.INuGetProjectService GetNuGetService(EnvDTE.Project proj) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + // Cache these to avoid the overhead of looking them up multiple times for the same solution. this.componentModel ??= this.GetService(proj.DTE, typeof(SComponentModel)) as IComponentModel; diff --git a/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioTextManipulation.cs b/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioTextManipulation.cs index 53478666..b78f715c 100644 --- a/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioTextManipulation.cs +++ b/VSIX/RapidXamlToolkit/VisualStudioIntegration/VisualStudioTextManipulation.cs @@ -26,6 +26,8 @@ public void RemoveInActiveDocOnLine(string find, int lineNumber) public void ReplaceInActiveDocOnLine(string find, string replace, int lineNumber) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { var matches = TextDocumentHelper.FindMatches(txtDoc, find); @@ -47,6 +49,8 @@ public void ReplaceInActiveDocOnLine(string find, string replace, int lineNumber public void ReplaceInActiveDocOnLineOrAbove(string find, string replace, int lineNumber) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { var matches = TextDocumentHelper.FindMatches(txtDoc, find); @@ -77,6 +81,8 @@ public void ReplaceInActiveDoc(string find, string replace, int startIndex, int public void ReplaceInActiveDoc(List<(string Find, string Replace)> replacements, int startIndex, int endIndex, Dictionary exclusions = null) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { // Have to implement search and replace directly as built-in functionality doesn't provide the control to only replace within the desired area @@ -116,6 +122,8 @@ public void ReplaceInActiveDoc(List<(string Find, string Replace)> replacements, public void InsertIntoActiveDocumentOnNextLine(string text, int pos) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { txtDoc.Selection.MoveToAbsoluteOffset(pos); @@ -127,11 +135,17 @@ public void InsertIntoActiveDocumentOnNextLine(string text, int pos) public void InsertIntoActiveDocOnLineAfterClosingTag(int openingAngleBracketLineNumber, string toInsert) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is TextDocument txtDoc) { var matches = TextDocumentHelper.FindMatches(txtDoc, ">"); - var matchPoint = matches.FirstOrDefault(m => m.Line >= openingAngleBracketLineNumber); + var matchPoint = matches.FirstOrDefault(m => + { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + return m.Line >= openingAngleBracketLineNumber; + }); if (matchPoint is not null) { @@ -144,6 +158,8 @@ public void InsertIntoActiveDocOnLineAfterClosingTag(int openingAngleBracketLine // Track the return value to know whether to end/close the UndoContext. public bool StartSingleUndoOperation(string name) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (!this.Dte.UndoContext.IsOpen) { this.Dte.UndoContext.Open(name); @@ -155,11 +171,15 @@ public bool StartSingleUndoOperation(string name) public void EndSingleUndoOperation() { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + this.Dte.UndoContext.Close(); } public void InsertAtEndOfLine(int lineNumber, string toInsert) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { txtDoc.Selection.MoveToLineAndOffset(lineNumber, 1); @@ -170,6 +190,8 @@ public void InsertAtEndOfLine(int lineNumber, string toInsert) public void DeleteFromEndOfLine(int lineNumber, int charsToDelete) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { txtDoc.Selection.MoveToLineAndOffset(lineNumber, 1); @@ -180,6 +202,8 @@ public void DeleteFromEndOfLine(int lineNumber, int charsToDelete) public void AddXmlnsAliasToActiveDoc(string alias, string value) { + Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread(); + if (this.Dte.ActiveDocument.Object("TextDocument") is EnvDTE.TextDocument txtDoc) { var matches = TextDocumentHelper.FindMatches(txtDoc, ">");