Search code examples
c#visual-studioenvdteattach-to-process

How to automatically attach process to a specific instance of VS?


I am trying to attach a process to VS by using this code:

DTE dte = (DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE.11.0");
EnvDTE.Processes pro = dte.Debugger.LocalProcesses;

foreach (EnvDTE.Process p in pro)
{
   if (p.ProcessID == num)
   {
      p.Attach();
      return;
   }
   else
   {
      continue;
   }
}

My problem is that I can't control to which instance of VS it gets attach to. usually its the first VS window that I opened.

How can I get a list of all the open VS instances? Thank you very much in advance!


Solution

  • The only way to distinguish between running VS instances is by selecting them by the solution they have loaded. As luck would have it (wasn't luck), VS also exposes the EnvDTE.Solution object in the running object table. You can use Roman's RotView-Win32.exe utility to see what that looks like.

    Some sample code that iterates the ROT and returns all active solutions:

    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Runtime.InteropServices.ComTypes;
    
    public class VSSolution {
        public string Name { get; set; }
        public EnvDTE.Solution Solution { get; set; }
    }
    
    public static class VSAutomation {
        public static List<VSSolution> GetRunningSolutions() {
            var instances = new List<VSSolution>();
            // Get running object table reference iterator
            IRunningObjectTable Rot;
            int hr = GetRunningObjectTable(0, out Rot);
            if (hr < 0) throw new COMException("No rot?", hr);
            IEnumMoniker monikerEnumerator;
            Rot.EnumRunning(out monikerEnumerator);
    
            // And iterate
            IntPtr pNumFetched = new IntPtr();
            IMoniker[] monikers = new IMoniker[1];
            while (monikerEnumerator.Next(1, monikers, pNumFetched) == 0) {
                IBindCtx bindCtx;
                int hr2 = CreateBindCtx(0, out bindCtx);
                if (hr < 0) continue;
                // Check if display ends with ".sln"
                string displayName;
                monikers[0].GetDisplayName(bindCtx, null, out displayName);
                if (displayName.EndsWith(".sln", StringComparison.CurrentCultureIgnoreCase)) {
                    object obj;
                    Rot.GetObject(monikers[0], out obj);
                    if (obj is EnvDTE.Solution) {
                        instances.Add(new VSSolution { Name = displayName, Solution = (EnvDTE.Solution)obj });
                    }
                    else Marshal.ReleaseComObject(obj);
                }
            }
            return instances;
        }
        [DllImport("ole32.dll")]
        private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
        [DllImport("ole32.dll")]
        private static extern int GetRunningObjectTable(int reserved, out IRunningObjectTable prot);
    }
    

    Sample usage as tested:

        foreach (var sln in GetRunningSolutions()) {
            if (sln.Name.EndsWith("ConsoleApplication1.sln")) {
                var dte = sln.Solution.DTE;
                // etc...
            }
        }