Запуск экземпляров Visual Studio 2010 и программное присоединение к процессу?


У меня есть приложение WinForms (.net 3.5), которое отображает список процессов.

Я хотел бы иметь возможность подключиться к одному из этих процессов. У меня запущено несколько экземпляров Visual Studio 2010, и я хотел бы создать список/выпадающее меню, где я выбираю один из этих экземпляров, а затем присоединяю к нему отладчик.

Получение экземпляров VS2010 не должно быть слишком сложным, но я понятия не имею, как вызвать команду "attach to process". Я хочу, чтобы избежать sendkeys будет-тип решения, поэтому мне просто интересно, есть ли какой-то способ сделать это?

Edit: для уточнения: я хочу использовать конкретный запуск VS2010 для отладки внешнего приложения.

2 3

2 ответа:

Один из подходов заключается в использовании EnvDTE, который является интерфейсом автоматизации COM для Visual Studio:

Http://msdn.microsoft.com/en-us/library/envdte (VS.100).aspx

Вы можете получить интерфейсы автоматизации для запуска экземпляров Visual Studio, порыбачив в таблице Running Objects (ROT). После того, как у вас есть экземпляр интерфейса, вы можете автоматизировать выбранный экземпляр Visual Studio для присоединения к процессу, который вы хотите.

Ниже приведен основной пример того, как сделать это. Вам нужно будет добавить ссылку на ваш проект в EnvDTE. Эта сборка находится в следующем месте на моей машине:

C:\Program файлы (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\EnvDTE.dll

Обновлено

Обновлено, чтобы привести пример получения интерфейса автоматизации экземпляра Visual Studio по идентификатору процесса.

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

Ответ Тима Ллойда отлично работает. Вот небольшая модификация, которая делает консольную программу, которая присоединяет отладчик к программе, указанной в командной строке как регулярное выражение без учета регистра. Это довольно просто, поэтому предполагается, что работает только один экземпляр Visual Studio и только один экземпляр процесса, к которому вы хотите подключиться.

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