├── .gitignore ├── DebugSingleThread.sln ├── DebugSingleThread ├── DebugSingleThread.csproj ├── DebugSingleThread.csproj.user ├── Properties │ └── AssemblyInfo.cs └── source.extension.vsixmanifest ├── DebugSingleThread2022 ├── DebugSingleThread2022.csproj ├── Properties │ └── AssemblyInfo.cs └── source.extension.vsixmanifest ├── DebugSingleThreadShared ├── DebugSingleThreadPackage.cs ├── DebugSingleThreadShared.projitems ├── DebugSingleThreadShared.shproj ├── GlobalSuppressions.cs ├── Guids.cs ├── MyCommand.cs └── PkgCmdID.cs ├── DebugSingleThreadSharedFolder ├── Assets │ ├── Icon.png │ ├── Images_32bit.psd │ ├── flake.png │ ├── image1.png │ └── image2.png ├── DebugSingleThread.vsct ├── Icon.png ├── Images_32bit.png ├── Key.snk ├── License.txt └── Screenshot.png ├── LICENSE.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | *.suo 3 | *.csproj.user 4 | *.bak 5 | [Bb]in 6 | obj/ 7 | [Rr]elease*/ 8 | _ReSharper*/ 9 | [Tt]humbs.db 10 | [Tt]est[Rr]esult* 11 | [Bb]uild[Ll]og.* 12 | *.[Pp]ublish.xml 13 | *.resharper 14 | var/ 15 | *.sln.ide 16 | Reviews 17 | packages/ -------------------------------------------------------------------------------- /DebugSingleThread.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.0.31903.59 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugSingleThread", "DebugSingleThread\DebugSingleThread.csproj", "{78F93759-FEFC-4E82-A3EB-A46F5D93875F}" 7 | EndProject 8 | Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "DebugSingleThreadShared", "DebugSingleThreadShared\DebugSingleThreadShared.shproj", "{C6A85F7F-C9C7-4EBC-A727-EDF438675745}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DebugSingleThread2022", "DebugSingleThread2022\DebugSingleThread2022.csproj", "{F11AAC77-729B-44DD-BBC4-6011B9066283}" 11 | EndProject 12 | Global 13 | GlobalSection(SharedMSBuildProjectFiles) = preSolution 14 | DebugSingleThreadShared\DebugSingleThreadShared.projitems*{78f93759-fefc-4e82-a3eb-a46f5d93875f}*SharedItemsImports = 4 15 | DebugSingleThreadShared\DebugSingleThreadShared.projitems*{c6a85f7f-c9c7-4ebc-a727-edf438675745}*SharedItemsImports = 13 16 | DebugSingleThreadShared\DebugSingleThreadShared.projitems*{f11aac77-729b-44dd-bbc4-6011b9066283}*SharedItemsImports = 4 17 | EndGlobalSection 18 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 19 | Debug|Any CPU = Debug|Any CPU 20 | Debug|x86 = Debug|x86 21 | Release|Any CPU = Release|Any CPU 22 | Release|x86 = Release|x86 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Debug|x86.ActiveCfg = Debug|x86 28 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Debug|x86.Build.0 = Debug|x86 29 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Release|Any CPU.ActiveCfg = Release|Any CPU 30 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Release|Any CPU.Build.0 = Release|Any CPU 31 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Release|x86.ActiveCfg = Release|x86 32 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F}.Release|x86.Build.0 = Release|x86 33 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 34 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Debug|Any CPU.Build.0 = Debug|Any CPU 35 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Debug|x86.ActiveCfg = Debug|x86 36 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Debug|x86.Build.0 = Debug|x86 37 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Release|x86.ActiveCfg = Release|x86 40 | {F11AAC77-729B-44DD-BBC4-6011B9066283}.Release|x86.Build.0 = Release|x86 41 | EndGlobalSection 42 | GlobalSection(SolutionProperties) = preSolution 43 | HideSolutionNode = FALSE 44 | EndGlobalSection 45 | GlobalSection(ExtensibilityGlobals) = postSolution 46 | SolutionGuid = {A731CA39-9077-445B-9839-028241FF321E} 47 | EndGlobalSection 48 | EndGlobal 49 | -------------------------------------------------------------------------------- /DebugSingleThread/DebugSingleThread.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 15.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | publish\ 8 | true 9 | Disk 10 | false 11 | Foreground 12 | 7 13 | Days 14 | false 15 | false 16 | true 17 | 0 18 | 1.0.0.%2a 19 | false 20 | false 21 | true 22 | 23 | 24 | true 25 | 26 | 27 | ..\DebugSingleThreadSharedFolder\Key.snk 28 | 29 | 30 | true 31 | 32 | 33 | 34 | Debug 35 | AnyCPU 36 | 2.0 37 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 38 | {78F93759-FEFC-4E82-A3EB-A46F5D93875F} 39 | Library 40 | Properties 41 | ErwinMayerLabs.DebugSingleThread 42 | DebugSingleThread 43 | v4.6 44 | true 45 | true 46 | true 47 | false 48 | false 49 | true 50 | true 51 | Program 52 | $(DevEnvDir)devenv.exe 53 | /rootsuffix Exp 54 | 55 | 56 | true 57 | full 58 | false 59 | bin\Debug\ 60 | DEBUG;TRACE 61 | prompt 62 | 4 63 | 64 | 65 | pdbonly 66 | true 67 | bin\Release\ 68 | TRACE 69 | prompt 70 | 4 71 | false 72 | 73 | 74 | 75 | 76 | 77 | 78 | Key.snk 79 | 80 | 81 | Designer 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | False 94 | Microsoft .NET Framework 4.6.2 %28x86 and x64%29 95 | true 96 | 97 | 98 | False 99 | .NET Framework 3.5 SP1 100 | false 101 | 102 | 103 | 104 | 105 | 16.10.10 106 | runtime; build; native; contentfiles; analyzers; buildtransitive 107 | all 108 | 109 | 110 | 14.3.25407 111 | 112 | 113 | 10.0.4 114 | 115 | 116 | 117 | 118 | DebugSingleThread.vsct 119 | Menus.ctmenu 120 | Designer 121 | 122 | 123 | Screenshot.png 124 | true 125 | 126 | 127 | Icon.png 128 | true 129 | 130 | 131 | Images_32bit.png 132 | 133 | 134 | License.txt 135 | true 136 | 137 | 138 | 139 | 140 | 141 | 142 | if "$(ConfigurationName)" neq "Release" ( 143 | exit /b 0 144 | ) 145 | 146 | if not exist "$(SolutionDir)Releases\" ( 147 | mkdir "$(SolutionDir)Releases\" 148 | if errorlevel 1 goto Error 149 | ) 150 | 151 | copy /Y "$(TargetDir)$(TargetName).vsix" "$(SolutionDir)Releases\$(TargetName)2019 (new).vsix" 152 | if errorlevel 1 goto Error 153 | 154 | exit /b 0 155 | 156 | :Error 157 | exit /b 1 158 | 159 | 160 | 167 | -------------------------------------------------------------------------------- /DebugSingleThread/DebugSingleThread.csproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | en-US 11 | false 12 | true 13 | 14 | 15 | C:\Program Files %28x86%29\Microsoft Visual Studio\2019\Community\Common7\IDE\devenv.exe 16 | 2fd9d919 17 | 18 | -------------------------------------------------------------------------------- /DebugSingleThread/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DebugSingleThread")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Erwin Mayer Labs")] 12 | [assembly: AssemblyProduct("DebugSingleThread")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("2.0.0.0")] 33 | [assembly: AssemblyFileVersion("2.0.0.0")] 34 | -------------------------------------------------------------------------------- /DebugSingleThread/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug Single Thread 6 | This Visual Studio extension adds two shortcuts and toolbar buttons to allow developers to easily focus on single threads while debugging multi-threaded applications. 7 | 8 | It dramatically reduces the need to manually go into the Threads window to freeze/thaw all threads but the one that needs to be followed, and therefore helps improve productivity. 9 | 10 | Features: 11 | - Restrict further execution to the current thread only. Will freeze all other threads. Shortcut: CTRL+T+T or snowflake button. Click button again to thaw all other threads. Known frozen threads before the command runs (e.g. due to a breakpoint) will be remembered and not thawed. If this is not what you expect, please let me know so I can add the option to customize behavior. 12 | - Switch to the next single thread (based on ManagedID). Will change current thread and freeze all other threads. Shortcut: CTRL+T+J or Next button. 13 | https://erwinmayer.com/labs/visual-studio-2010-extension-debug-single-thread 14 | License.txt 15 | Icon.png 16 | Screenshot.png 17 | Multi-Threaded, Debugging, Single Thread, Thread Focus 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /DebugSingleThread2022/DebugSingleThread2022.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 17.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | true 9 | 10 | 11 | ..\DebugSingleThreadSharedFolder\Key.snk 12 | 13 | 14 | 15 | Debug 16 | AnyCPU 17 | 2.0 18 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 19 | {F11AAC77-729B-44DD-BBC4-6011B9066283} 20 | Library 21 | Properties 22 | ErwinMayerLabs.DebugSingleThread 23 | DebugSingleThread2022 24 | v4.7.2 25 | true 26 | true 27 | true 28 | false 29 | false 30 | true 31 | true 32 | Program 33 | $(DevEnvDir)devenv.exe 34 | /rootsuffix Exp 35 | 36 | 37 | true 38 | full 39 | false 40 | bin\Debug\ 41 | DEBUG;TRACE 42 | prompt 43 | 4 44 | 45 | 46 | pdbonly 47 | true 48 | bin\Release\ 49 | TRACE 50 | prompt 51 | 4 52 | 53 | 54 | 55 | 56 | 57 | 58 | Key.snk 59 | 60 | 61 | Designer 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | DebugSingleThread.vsct 75 | Menus.ctmenu 76 | Designer 77 | 78 | 79 | Icon.png 80 | true 81 | 82 | 83 | Images_32bit.png 84 | 85 | 86 | License.txt 87 | true 88 | 89 | 90 | Screenshot.png 91 | true 92 | 93 | 94 | 95 | 96 | 97 | 98 | if "$(ConfigurationName)" neq "Release" ( 99 | exit /b 0 100 | ) 101 | 102 | if not exist "$(SolutionDir)Releases\" ( 103 | mkdir "$(SolutionDir)Releases\" 104 | if errorlevel 1 goto Error 105 | ) 106 | 107 | copy /Y "$(TargetDir)$(TargetName).vsix" "$(SolutionDir)Releases\$(TargetName) (new).vsix" 108 | if errorlevel 1 goto Error 109 | 110 | exit /b 0 111 | 112 | :Error 113 | exit /b 1 114 | 115 | 116 | 123 | -------------------------------------------------------------------------------- /DebugSingleThread2022/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("DebugSingleThread2022")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("DebugSingleThread2022")] 13 | [assembly: AssemblyCopyright("")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Build and Revision Numbers 30 | // by using the '*' as shown below: 31 | // [assembly: AssemblyVersion("1.0.*")] 32 | [assembly: AssemblyVersion("1.0.0.0")] 33 | [assembly: AssemblyFileVersion("1.0.0.0")] 34 | -------------------------------------------------------------------------------- /DebugSingleThread2022/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug Single Thread 6 | This Visual Studio extension adds two shortcuts and toolbar buttons to allow developers to easily focus on single threads while debugging multi-threaded applications. 7 | 8 | It dramatically reduces the need to manually go into the Threads window to freeze/thaw all threads but the one that needs to be followed, and therefore helps improve productivity. 9 | 10 | Features: 11 | - Restrict further execution to the current thread only. Will freeze all other threads. Shortcut: CTRL+T+T or snowflake button. Click button again to thaw all other threads. Known frozen threads before the command runs (e.g. due to a breakpoint) will be remembered and not thawed. If this is not what you expect, please let me know so I can add the option to customize behavior. 12 | - Switch to the next single thread (based on ManagedID). Will change current thread and freeze all other threads. Shortcut: CTRL+T+J or Next button. 13 | https://erwinmayer.com/labs/visual-studio-2010-extension-debug-single-thread 14 | License.txt 15 | Icon.png 16 | Screenshot.png 17 | Multi-Threaded, Debugging, Single Thread, Thread Focus 18 | 19 | 20 | 21 | amd64 22 | 23 | 24 | amd64 25 | 26 | 27 | amd64 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /DebugSingleThreadShared/DebugSingleThreadPackage.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio; 2 | using Microsoft.VisualStudio.Shell; 3 | using System; 4 | using System.Diagnostics; 5 | using System.Globalization; 6 | using System.Runtime.InteropServices; 7 | using System.Threading; 8 | using Task = System.Threading.Tasks.Task; 9 | /* 10 | * TODO: 11 | * - Add context menu item in the Threads window (Freeze all but this thread) 12 | * Sources: 13 | * http://msdn.microsoft.com/en-us/library/cc138589.aspx 14 | * http://msdn.microsoft.com/en-us/library/bb164715.aspx 15 | * http://findicons.com/icon/137367/flake?width=256# 16 | * http://www.iconspedia.com/icon/snow-flake.html 17 | * http://www.iconspedia.com/search/next/ 18 | */ 19 | namespace ErwinMayerLabs.DebugSingleThread { 20 | /// 21 | /// This is the class that implements the package exposed by this assembly. 22 | /// 23 | /// 24 | /// 25 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 26 | /// is to implement the IVsPackage interface and register itself with the shell. 27 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 28 | /// to do it: it derives from the Package class that provides the implementation of the 29 | /// IVsPackage interface and uses the registration attributes defined in the framework to 30 | /// register itself and its components with the shell. These attributes tell the pkgdef creation 31 | /// utility what data to put into .pkgdef file. 32 | /// 33 | /// 34 | /// To get loaded into VS, the package must be referred by <Asset Type="Microsoft.VisualStudio.VsPackage" ...> in .vsixmanifest file. 35 | /// 36 | /// 37 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 38 | // This attribute is used to register the informations needed to show the this package 39 | // in the Help/About dialog of Visual Studio. 40 | [InstalledProductRegistration("#110", "#112", "2.0", IconResourceID = 400)] 41 | [ProvideAutoLoad(VSConstants.UICONTEXT.Debugging_string, PackageAutoLoadFlags.BackgroundLoad)] 42 | // This attribute is needed to let the shell know that this package exposes some menus. 43 | [ProvideMenuResource("Menus.ctmenu", 1)] 44 | [Guid(GuidList.guidDebugSingleThreadPkgString)] 45 | public sealed class DebugSingleThreadPackage : AsyncPackage { 46 | /// 47 | /// Default constructor of the package. 48 | /// Inside this method you can place any initialization code that does not require 49 | /// any Visual Studio service because at this point the package object is created but 50 | /// not sited yet inside Visual Studio environment. The place to do all the other 51 | /// initialization is the Initialize method. 52 | /// 53 | public DebugSingleThreadPackage() { 54 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this)); 55 | } 56 | 57 | #region Package Members 58 | /// 59 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 60 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 61 | /// 62 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. 63 | /// A provider for progress updates. 64 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. 65 | protected override async Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) { 66 | // When initialized asynchronously, the current thread may be a background thread at this point. 67 | // Do any initialization that requires the UI thread after switching to the UI thread. 68 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 69 | //await MyCommand.InitializeAsync(this); 70 | 71 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this)); 72 | await MyCommand.InitializeAsync(this); 73 | } 74 | 75 | #endregion 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /DebugSingleThreadShared/DebugSingleThreadShared.projitems: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(MSBuildAllProjects);$(MSBuildThisFileFullPath) 5 | true 6 | c6a85f7f-c9c7-4ebc-a727-edf438675745 7 | 8 | 9 | DebugSingleThreadShared 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /DebugSingleThreadShared/DebugSingleThreadShared.shproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | c6a85f7f-c9c7-4ebc-a727-edf438675745 5 | 14.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /DebugSingleThreadShared/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] 12 | -------------------------------------------------------------------------------- /DebugSingleThreadShared/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace ErwinMayerLabs.DebugSingleThread 6 | { 7 | static class GuidList 8 | { 9 | public const string guidDebugSingleThreadPkgString = "9fb8ed8b-b44a-4076-b677-cbeb7b834afb"; 10 | public const string guidDebugSingleThreadCmdSetString = "2d00031b-eb83-4527-a3d0-9e1c14c1be97"; 11 | 12 | public static readonly Guid guidDebugSingleThreadCmdSet = new Guid(guidDebugSingleThreadCmdSetString); 13 | }; 14 | } -------------------------------------------------------------------------------- /DebugSingleThreadShared/MyCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Design; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.Shell; 6 | using Task = System.Threading.Tasks.Task; 7 | using Thread = EnvDTE.Thread; 8 | 9 | namespace ErwinMayerLabs.DebugSingleThread { 10 | /// 11 | /// Command handler 12 | /// 13 | internal sealed class MyCommand { 14 | private static EnvDTE.DTE dte; 15 | private static OleMenuCommand FocusCmd; 16 | private static OleMenuCommand SwitchCmd; 17 | 18 | /// 19 | /// Initializes the singleton instance of the command. 20 | /// 21 | /// Owner package, not null. 22 | public static async Task InitializeAsync(AsyncPackage package) { 23 | await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken); 24 | 25 | //Add our command handlers for menu (commands must exist in the .vsct file) 26 | if (await package.GetServiceAsync(typeof(IMenuCommandService)) is OleMenuCommandService commandService) { 27 | // Create the command for the menu item. 28 | var menuCommandID1 = new CommandID(GuidList.guidDebugSingleThreadCmdSet, (int)PkgCmdIDList.FocusOnCurrentThreadCmd); 29 | var menuCommandID2 = new CommandID(GuidList.guidDebugSingleThreadCmdSet, (int)PkgCmdIDList.SwitchToNextThreadCmd); 30 | FocusCmd = new OleMenuCommand(FocusOnCurrentThread, menuCommandID1); 31 | SwitchCmd = new OleMenuCommand(SwitchToNextThread, menuCommandID2); 32 | FocusCmd.BeforeQueryStatus += OnBeforeQueryStatus; 33 | SwitchCmd.BeforeQueryStatus += OnBeforeQueryStatus; 34 | 35 | commandService.AddCommand(FocusCmd); 36 | commandService.AddCommand(SwitchCmd); 37 | 38 | dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); 39 | OnBeforeQueryStatus(FocusCmd, null); 40 | OnBeforeQueryStatus(SwitchCmd, null); 41 | } 42 | } 43 | 44 | // Could serve to remember threads that were frozen prior to Focusing, but it is likely that users will want to discard previous freezes. 45 | // If needed, see if we can add button to remember and not touch existing frozen threads (off by default). Or check if there is a ModifierKeys upon clicking on the button. 46 | // This may not work correctly if thread IDs are recycled within a focus/unfocus session (should be quite unlikely though, no easy workaround beside not remembering previously frozen threads at all). 47 | private static HashSet IgnoredThreads = new HashSet(); 48 | private static bool IsFocused = false; 49 | /// 50 | /// This function is the callback used to execute a command when the a menu item is clicked. 51 | /// See the Initialize method to see how the menu item is associated to this function using 52 | /// the OleMenuCommandService service and the MenuCommand class. 53 | /// 54 | private static void FocusOnCurrentThread(object sender, EventArgs e) { 55 | try { 56 | ThreadHelper.ThrowIfNotOnUIThread(); 57 | if (dte.Debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgRunMode) dte.Debugger.Break(); 58 | foreach (Thread thread in dte.Debugger.CurrentProgram.Threads) { 59 | try { 60 | if (!IsFocused) { 61 | if (thread.ID == dte.Debugger.CurrentThread.ID) { 62 | if (thread.IsFrozen) { 63 | thread.Thaw(); 64 | } 65 | } 66 | else if (thread.IsAlive) { 67 | if (thread.IsFrozen) { 68 | IgnoredThreads.Add(thread.ID); 69 | } 70 | else { 71 | thread.Freeze(); 72 | } 73 | } 74 | } 75 | else { 76 | if (thread.IsFrozen && !IgnoredThreads.Contains(thread.ID)) { 77 | thread.Thaw(); 78 | } 79 | } 80 | } 81 | catch (Exception) { 82 | // ignored 83 | } 84 | } 85 | if (!IsFocused) { 86 | IsFocused = true; 87 | FocusCmd.Checked = true; 88 | SwitchCmd.Enabled = true; 89 | } 90 | else { 91 | IgnoredThreads.Clear(); 92 | IsFocused = false; 93 | FocusCmd.Checked = false; 94 | SwitchCmd.Enabled = false; 95 | } 96 | } 97 | catch (Exception) { 98 | // ignored 99 | } 100 | } 101 | 102 | private static void SwitchToNextThread(object sender, EventArgs e) { 103 | var remainingAttempts = 1000; 104 | try { 105 | ThreadHelper.ThrowIfNotOnUIThread(); 106 | if (dte.Debugger.CurrentMode != EnvDTE.dbgDebugMode.dbgBreakMode) return; 107 | IsFocused = true; 108 | var terminatedThreads = new HashSet(); 109 | while (true) { 110 | var liveThreads = dte.Debugger.CurrentProgram.Threads.Cast().Where(t => t.IsAlive && t.Name != "[Thread Destroyed]" && !terminatedThreads.Contains(t.ID)).OrderBy(t => t.ID).ToList(); 111 | // It is still to be found how to match an EnvDTE thread to a System.Threading.Thread to sort based on the ManagedThreadID, if possible and desirable at all. 112 | if (!liveThreads.Any()) return; 113 | var nextThread = liveThreads.FirstOrDefault(t => t.ID > dte.Debugger.CurrentThread.ID) ?? liveThreads.First(); 114 | foreach (var thread in liveThreads.Where(thread => !thread.IsFrozen)) { 115 | try { 116 | thread.Freeze(); 117 | } 118 | catch (Exception) { 119 | // ignored 120 | } 121 | } 122 | try { 123 | nextThread.Thaw(); 124 | //IgnoredThreads.Remove(nextThread.ID); 125 | } 126 | catch (Exception) { 127 | if (remainingAttempts-- > 0) { 128 | terminatedThreads.Add(nextThread.ID); 129 | continue; 130 | } 131 | // ignored 132 | return; 133 | } 134 | dte.Debugger.CurrentThread = nextThread; 135 | break; 136 | } 137 | } 138 | catch (Exception) { 139 | // ignored 140 | } 141 | } 142 | 143 | private static void OnBeforeQueryStatus(object sender, EventArgs e) { 144 | if (sender is OleMenuCommand myCommand) { 145 | if (dte.Mode == EnvDTE.vsIDEMode.vsIDEModeDebug) { 146 | if (myCommand.CommandID.Guid != SwitchCmd.CommandID.Guid || dte.Debugger.CurrentMode == EnvDTE.dbgDebugMode.dbgBreakMode) { 147 | if (myCommand.CommandID.Guid != SwitchCmd.CommandID.Guid && IsFocused) { 148 | myCommand.Checked = true; 149 | } 150 | myCommand.Enabled = true; 151 | myCommand.Visible = true; 152 | } 153 | else { 154 | myCommand.Enabled = false; 155 | myCommand.Visible = false; 156 | } 157 | } 158 | else { 159 | IsFocused = false; 160 | myCommand.Checked = false; 161 | myCommand.Enabled = false; 162 | myCommand.Visible = false; 163 | } 164 | } 165 | } 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /DebugSingleThreadShared/PkgCmdID.cs: -------------------------------------------------------------------------------- 1 | // PkgCmdID.cs 2 | // MUST match PkgCmdID.h 3 | using System; 4 | 5 | namespace ErwinMayerLabs.DebugSingleThread 6 | { 7 | static class PkgCmdIDList 8 | { 9 | public const uint FocusOnCurrentThreadCmd = 0x100; 10 | public const uint SwitchToNextThreadCmd = 0x101; 11 | }; 12 | } -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Assets/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Assets/Icon.png -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Assets/Images_32bit.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Assets/Images_32bit.psd -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Assets/flake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Assets/flake.png -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Assets/image1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Assets/image1.png -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Assets/image2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Assets/image2.png -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/DebugSingleThread.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 28 | 29 | 30 | 32 | 33 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 50 | 51 | 58 | 69 | 80 | 81 | 82 | 83 | 84 | 85 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Icon.png -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Images_32bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Images_32bit.png -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Key.snk -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2011 Erwin Mayer 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /DebugSingleThreadSharedFolder/Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mayerwin/vs-debug-single-thread/a3646b291e69655dcba42236b9179943378f9ef5/DebugSingleThreadSharedFolder/Screenshot.png -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2011 Erwin Mayer 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Debug Single Thread - Visual Studio Extension 2 | ![Screenshot](/DebugSingleThreadSharedFolder/Screenshot.png?raw=true "Screenshot") 3 | 4 | This is the official repository for: 5 | https://marketplace.visualstudio.com/items?itemName=mayerwin.DebugSingleThread 6 | 7 | Initial release page here: 8 | http://erwinmayer.com/labs/visual-studio-2010-extension-debug-single-thread 9 | 10 | **Description** 11 | 12 | This Visual Studio extension adds two shortcuts and toolbar buttons to allow developers to easily focus on single threads while debugging multi-threaded applications. 13 | It dramatically reduces the need to manually go into the Threads window to freeze/thaw all threads but the one that needs to be followed, and therefore helps improve productivity. 14 | 15 | **Features** 16 | - Restrict further execution to the current thread only. Will freeze all other threads. Shortcut: `CTRL+T+T` or `snowflake` button. Click button again to thaw all other threads. Known frozen threads before the command runs (e.g. due to a breakpoint) will be remembered and not thawed. If this is not what you expect, please let me know so I can add an option to customize behavior. 17 | - Switch to the next single thread (based on ID). Will change current thread and freeze all other threads. Shortcut: `CTRL+T+J` or `Next` button. 18 | 19 | **Supported editions** 20 | 21 | Visual Studio 2015, 2017, 2019, 2022+. 22 | 23 | Visual Studio 2017 and 2019 support has been temporarily dropped due to limitations imposed by Microsoft's Visual Studio Marketplace. The last stable release (4.0.0) supporting Visual Studio 2019 can however be downloaded [here](https://github.com/mayerwin/vs-debug-single-thread/releases/tag/4.0.0). 24 | 25 | Visual Studio 2012 and 2013 support has been dropped due to new requirements imposed by Microsoft to speed up loading time with AsyncPackage. The last stable release supporting Visual Studio 2012 and 2013 can however be downloaded [here](https://github.com/mayerwin/vs-debug-single-thread/releases/tag/1.1.3_2012-2017). 26 | 27 | Visual Studio 2010 support has been dropped due to limitations imposed by Microsoft's VSIX format to support Visual Studio 2017. The last stable release supporting Visual Studio 2010 can however be downloaded [here](https://github.com/mayerwin/vs-debug-single-thread/releases/tag/1.1.3). 28 | 29 | You are welcome to contribute to this project! 30 | --------------------------------------------------------------------------------