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

2

3

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.

Predesignate answered 16/7, 2010 at 20:6 Comment(3)
Do you want to attach to the VS2010 instances or do you want to use 1 of them as the debugger?Spearman
I am not too sure if this will be helpful so I am not posting this as a full solution but I would start by looking in Microsoft.VisualStudio.Debugger.Interop From the Visual Studio SDK microsoft.com/downloads/…Bluma
@Henk I want to use VS to attach to a separate applicationPredesignate
A
6

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;
    }
}
}
Airscrew answered 16/7, 2010 at 20:46 Comment(2)
@Michael I have not used this technique since VS2005, and although I coded this up against 2010 and tested it, did you try it and come up against issues that might be helpful? I will be introducing a feature in my application in the near future that relies on this approach and wondered if there were any 2010 got-yas you came across. Thanks.Airscrew
To be noted: TryGetVsInstance will not return the DTE object of an instance of Visual Studio which was not started as an administrator, if the calling script is called from a process with elevated rights.Implicit
H
1

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;
        }
    }
}
Hurwitz answered 10/4, 2014 at 21:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.