I have a WinForms Application (.net 3.5) which displays a list of processes.
I would like to开发者_StackOverflow社区 be able to attach to one of those processes. I have multiple Visual Studio 2010 instances running and I would like to create a List/Dropdown where I select one of those instances and then attach the Debugger to it.
Getting the VS2010 instances shouldn't be too hard, but I have no idea how to invoke the "attach to process" command. I want to avoid SendKeys-Type solutions, so I just wonder if there is some way to do that?
edit: To clarify: I want to use a specific running VS2010 to debug an external application.
One approach is to use EnvDTE which is the COM automation interface for Visual Studio:
http://msdn.microsoft.com/en-us/library/envdte(VS.100).aspx
You can get at automation interfaces for running instances of Visual Studio by fishing around in the Running Objects Table (ROT). Once you have an instance of the interface you can then automate a selected instance of Visual Studio to attach to a process you desire.
Below is a basic example of how to do this. You will need to add a reference to your project to EnvDTE. This assembly is at the following location on my machine:
C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\EnvDTE.dll
Updated
Updated to give example of getting Visual Studio instance automation interface by process ID.
using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE;
namespace VS2010EnvDte
{
internal class Program
{
[DllImport("ole32.dll")]
public static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("ole32.dll")]
public static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
private static void Main()
{
//ProcessId of the VS instance - hard-coded here.
int visualStudioProcessId = 5520;
_DTE visualStudioInstance;
if (TryGetVSInstance(visualStudioProcessId, out visualStudioInstance))
{
Process processToAttachTo = null;
//Find the process you want the VS instance to attach to...
foreach (Process process in visualStudioInstance.Debugger.LocalProcesses)
{
if (process.Name == @"C:\Users\chibacity\AppData\Local\Google\Chrome\Application\chrome.exe")
{
processToAttachTo = process;
break;
}
}
//Attach to the process.
if (processToAttachTo != null)
{
processToAttachTo.Attach();
}
}
}
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;
}
}
}
Tim Lloyd's answer works great. Here's a small modification that makes a console program that attaches the debugger to a program specified as a case-insensitive regular expression on the command line. It's pretty simple so assumes only one instance of Visual Studio running, and only one instance of the process you want to attach to.
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Text.RegularExpressions;
using EnvDTE;
namespace VstAttach {
internal static class Program {
[DllImport("ole32.dll")]
static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
[DllImport("ole32.dll")]
static extern int CreateBindCtx(int reserved, out IBindCtx ppbc);
private static void Main(string[] args) {
if (args.Length == 0)
throw new Exception("Syntax: VstAttach ProcessName (case insensitive regex)");
var vst = System.Diagnostics.Process.GetProcessesByName("devenv").FirstOrDefault();
if (vst == null)
throw new Exception("Visual Studio not found.");
var visualStudioProcessId = vst.Id;
_DTE visualStudioInstance;
if (TryGetVsInstance(visualStudioProcessId, out visualStudioInstance)) {
var processToAttachTo = visualStudioInstance.Debugger.LocalProcesses
.Cast<Process>()
.FirstOrDefault(process => Regex.IsMatch(process.Name, args[0], RegexOptions.IgnoreCase));
if (processToAttachTo != null) {
processToAttachTo.Attach();
}
}
}
private static bool TryGetVsInstance(int processId, out _DTE instance) {
var numFetched = IntPtr.Zero;
IRunningObjectTable runningObjectTable;
IEnumMoniker monikerEnumerator;
var 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;
}
}
}
精彩评论