Search code examples
.netvisual-studio-2010debuggingremoting

Getting running Visual Studio 2010 instances and programmatically attaching to a process?


I have a WinForms Application (.net 3.5) which displays a list of processes.

I would like to 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.


Solution

  • 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;
        }
    }
    }