-
-
Save atruskie/3813175 to your computer and use it in GitHub Desktop.
// -------------------------------------------------------------------------------------------------------------------- | |
// <copyright file="AutoAttachVs.cs" company="QutEcoacoustics"> | |
// All code in this file and all associated files are the copyright and property of the QUT Ecoacoustics Research Group (formerly MQUTeR, and formerly QUT Bioacoustics Research Group). | |
// </copyright> | |
// <summary> | |
// Example taken from this gist. | |
// </summary> | |
// -------------------------------------------------------------------------------------------------------------------- | |
#if DEBUG | |
namespace Acoustics.Shared.Debugging | |
{ | |
using System; | |
using System.Collections.Generic; | |
using System.Diagnostics.CodeAnalysis; | |
using System.IO; | |
using System.Linq; | |
using System.Runtime.InteropServices; | |
using System.Runtime.InteropServices.ComTypes; | |
using EnvDTE; | |
using DTEProcess = EnvDTE.Process; | |
using Process = System.Diagnostics.Process; | |
/// <summary> | |
/// Example taken from <a href="https://gist.github.com/3813175">this gist</a>. | |
/// </summary> | |
[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1305:FieldNamesMustNotUseHungarianNotation", | |
Justification = "Reviewed. Suppression is OK here.", Scope = "class")] | |
public static class VisualStudioAttacher | |
{ | |
[DllImport("ole32.dll")] | |
public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc); | |
[DllImport("ole32.dll")] | |
public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot); | |
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
public static extern bool SetForegroundWindow(IntPtr hWnd); | |
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] | |
public static extern IntPtr SetFocus(IntPtr hWnd); | |
public static string GetSolutionForVisualStudio(Process visualStudioProcess) | |
{ | |
_DTE visualStudioInstance; | |
if (TryGetVsInstance(visualStudioProcess.Id, out visualStudioInstance)) | |
{ | |
try | |
{ | |
return visualStudioInstance.Solution.FullName; | |
} | |
catch (Exception) | |
{ | |
} | |
} | |
return null; | |
} | |
public static Process GetAttachedVisualStudio(Process applicationProcess) | |
{ | |
IEnumerable<Process> visualStudios = GetVisualStudioProcesses(); | |
foreach (Process visualStudio in visualStudios) | |
{ | |
_DTE visualStudioInstance; | |
if (TryGetVsInstance(visualStudio.Id, out visualStudioInstance)) | |
{ | |
try | |
{ | |
foreach (Process debuggedProcess in visualStudioInstance.Debugger.DebuggedProcesses) | |
{ | |
if (debuggedProcess.Id == applicationProcess.Id) | |
{ | |
return debuggedProcess; | |
} | |
} | |
} | |
catch (Exception) | |
{ | |
} | |
} | |
} | |
return null; | |
} | |
/// <summary> | |
/// The method to use to attach visual studio to a specified process. | |
/// </summary> | |
/// <param name="visualStudioProcess"> | |
/// The visual studio process to attach to. | |
/// </param> | |
/// <param name="applicationProcess"> | |
/// The application process that needs to be debugged. | |
/// </param> | |
/// <exception cref="InvalidOperationException"> | |
/// Thrown when the application process is null. | |
/// </exception> | |
public static void AttachVisualStudioToProcess(Process visualStudioProcess, Process applicationProcess) | |
{ | |
_DTE visualStudioInstance; | |
if (TryGetVsInstance(visualStudioProcess.Id, out visualStudioInstance)) | |
{ | |
// Find the process you want the VS instance to attach to... | |
DTEProcess processToAttachTo = | |
visualStudioInstance.Debugger.LocalProcesses.Cast<DTEProcess>() | |
.FirstOrDefault(process => process.ProcessID == applicationProcess.Id); | |
// Attach to the process. | |
if (processToAttachTo != null) | |
{ | |
processToAttachTo.Attach(); | |
ShowWindow((int)visualStudioProcess.MainWindowHandle, 3); | |
SetForegroundWindow(visualStudioProcess.MainWindowHandle); | |
} | |
else | |
{ | |
throw new InvalidOperationException( | |
"Visual Studio process cannot find specified application '" + applicationProcess.Id + "'"); | |
} | |
} | |
} | |
/// <summary> | |
/// The get visual studio for solutions. | |
/// </summary> | |
/// <param name="solutionNames"> | |
/// The solution names. | |
/// </param> | |
/// <returns> | |
/// The <see cref="Process"/>. | |
/// </returns> | |
public static Process GetVisualStudioForSolutions(List<string> solutionNames) | |
{ | |
foreach (string solution in solutionNames) | |
{ | |
Process visualStudioForSolution = GetVisualStudioForSolution(solution); | |
if (visualStudioForSolution != null) | |
{ | |
return visualStudioForSolution; | |
} | |
} | |
return null; | |
} | |
/// <summary> | |
/// The get visual studio process that is running and has the specified solution loaded. | |
/// </summary> | |
/// <param name="solutionName"> | |
/// The solution name to look for. | |
/// </param> | |
/// <returns> | |
/// The visual studio <see cref="Process"/> with the specified solution name. | |
/// </returns> | |
public static Process GetVisualStudioForSolution(string solutionName) | |
{ | |
IEnumerable<Process> visualStudios = GetVisualStudioProcesses(); | |
foreach (Process visualStudio in visualStudios) | |
{ | |
_DTE visualStudioInstance; | |
if (TryGetVsInstance(visualStudio.Id, out visualStudioInstance)) | |
{ | |
try | |
{ | |
string actualSolutionName = Path.GetFileName(visualStudioInstance.Solution.FullName); | |
if (string.Compare( | |
actualSolutionName, | |
solutionName, | |
StringComparison.InvariantCultureIgnoreCase) == 0) | |
{ | |
return visualStudio; | |
} | |
} | |
catch (Exception) | |
{ | |
throw; | |
} | |
} | |
} | |
return null; | |
} | |
[DllImport("User32")] | |
private static extern int ShowWindow(int hwnd, int nCmdShow); | |
private static IEnumerable<Process> GetVisualStudioProcesses() | |
{ | |
Process[] processes = Process.GetProcesses(); | |
return processes.Where(o => o.ProcessName.Contains("devenv")); | |
} | |
private static bool TryGetVsInstance(int processId, out _DTE instance) | |
{ | |
IntPtr numFetched = IntPtr.Zero; | |
IRunningObjectTable runningObjectTable; | |
IEnumMoniker monikerEnumerator; | |
IMoniker[] monikers = new IMoniker[1]; | |
GetRunningObjectTable(0, out runningObjectTable); | |
runningObjectTable.EnumRunning(out monikerEnumerator); | |
monikerEnumerator.Reset(); | |
while (monikerEnumerator.Next(1, monikers, numFetched) == 0) | |
{ | |
IBindCtx ctx; | |
CreateBindCtx(0, out ctx); | |
string runningObjectName; | |
monikers[0].GetDisplayName(ctx, null, out runningObjectName); | |
object runningObjectVal; | |
runningObjectTable.GetObject(monikers[0], out runningObjectVal); | |
if (runningObjectVal is _DTE && runningObjectName.StartsWith("!VisualStudio")) | |
{ | |
int currentProcessId = int.Parse(runningObjectName.Split(':')[1]); | |
if (currentProcessId == processId) | |
{ | |
instance = (_DTE)runningObjectVal; | |
return true; | |
} | |
} | |
} | |
instance = null; | |
return false; | |
} | |
} | |
} | |
#endif |
@aakasheth95, I've updated the code so that it works with a newer Visual Studio API.
You can see an example usage here: https://github.com/QutEcoacoustics/audio-analysis/blob/089e1489f209b09ca60de7b4a516a9ae70518c51/src/AnalysisPrograms/Production/MainEntryUtilities.cs#L140-L184
Note: this is not a plugin. This code is embedded into an executable. When the executable runs, it loads a visual studio DLL and starts a debugging process on itself.
There are few occasions for which this code is appropriate.
I've found that I need to add this in order to compile this code.
<Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
<Reference Include="envdte80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<EmbedInteropTypes>True</EmbedInteropTypes>
</Reference>
Source: https://reviews.llvm.org/D31740
That's a good point. It depends on the version of Visual Studio you are using. My references (riddled with tech debt) include:
<Reference Include="envdte, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<SpecificVersion>False</SpecificVersion>
<EmbedInteropTypes>True</EmbedInteropTypes>
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\envdte.dll</HintPath>
</Reference>
<Reference Include="envdte100, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<EmbedInteropTypes>True</EmbedInteropTypes>
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\envdte100.dll</HintPath>
</Reference>
<Reference Include="envdte80, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<SpecificVersion>False</SpecificVersion>
<EmbedInteropTypes>True</EmbedInteropTypes>
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\envdte80.dll</HintPath>
</Reference>
<Reference Include="envdte90, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<SpecificVersion>False</SpecificVersion>
<EmbedInteropTypes>True</EmbedInteropTypes>
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\envdte90.dll</HintPath>
</Reference>
<Reference Include="envdte90a, Version=9.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<SpecificVersion>False</SpecificVersion>
<EmbedInteropTypes>True</EmbedInteropTypes>
<HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\Common7\IDE\PublicAssemblies\envdte90a.dll</HintPath>
</Reference>
@atruskie: Because you are obviously mostly the only one doing the same things as I do, I have to ask you one question about the performance. It seems to me that locating the LocalProcesses takes a very lon time on my machine. Did you experience the same problem?
@msedi It has been a long time since I've used or needed this solution. I don't remember it being slow though.
@atruskie: No problem. I was just asking ;-)
Could you please explain how to consume this code, I am beginner not much familiar with visual studio plugins
Thank you