Ответ 1
Edit:
GSerjo
предложили правильное решение. Я хотел бы поделиться несколькими мыслями о том, как улучшить его (и объяснение). Надеюсь, мой улучшенный ответ будет полезен для других, которые испытывают ту же проблему.
Прикрепление отладчика VS к процессу
Вручную
- Откройте диспетчер задач Windows (Ctrl + Shift + Esc).
- Перейдите на вкладку
Processes
. - Щелкните правой кнопкой мыши процесс.
- Выберите
Debug
.
Или, в Visual Studio выберите Debug > Attach to Process...
.
Результаты будут зависеть от того, имеете ли вы доступ к исходному коду.
Автоматически с С#
A Примечание: Следующий код является хрупким в том смысле, что определенные значения, такие как номер версии Visual Studio, жестко закодированы. Имейте это в виду если вы планируете распространять свою программу.
Прежде всего, добавьте ссылку на EnvDTE в свой проект (щелкните правой кнопкой мыши по папке с рекомендациями в проводнике решений, добавьте ссылку). В следующем коде я покажу только необычные директивы; нормальные, такие как using System
, опущены.
Поскольку вы взаимодействуете с COM, вам необходимо убедиться, что вы украсили ваш метод Main
(точка входа в приложение) с помощью STAThreadAttribute
.
Затем вам нужно определить IOleMessageFilter
Interface, который позволит вам взаимодействовать с определенными COM-методами (обратите внимание на ComImportAttribute
). Нам нужно получить доступ к фильтру сообщений, чтобы мы могли повторить попытку, если компонент Visual Studio COM блокирует один из наших вызовов.
using System.Runtime.InteropServices;
[ComImport, Guid("00000016-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IOleMessageFilter
{
[PreserveSig]
int HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo);
[PreserveSig]
int RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType);
[PreserveSig]
int MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType);
}
Теперь нам нужно реализовать этот интерфейс для обработки входящих сообщений:
public class MessageFilter : IOleMessageFilter
{
private const int Handled = 0, RetryAllowed = 2, Retry = 99, Cancel = -1, WaitAndDispatch = 2;
int IOleMessageFilter.HandleInComingCall(int dwCallType, IntPtr hTaskCaller, int dwTickCount, IntPtr lpInterfaceInfo)
{
return Handled;
}
int IOleMessageFilter.RetryRejectedCall(IntPtr hTaskCallee, int dwTickCount, int dwRejectType)
{
return dwRejectType == RetryAllowed ? Retry : Cancel;
}
int IOleMessageFilter.MessagePending(IntPtr hTaskCallee, int dwTickCount, int dwPendingType)
{
return WaitAndDispatch;
}
public static void Register()
{
CoRegisterMessageFilter(new MessageFilter());
}
public static void Revoke()
{
CoRegisterMessageFilter(null);
}
private static void CoRegisterMessageFilter(IOleMessageFilter newFilter)
{
IOleMessageFilter oldFilter;
CoRegisterMessageFilter(newFilter, out oldFilter);
}
[DllImport("Ole32.dll")]
private static extern int CoRegisterMessageFilter(IOleMessageFilter newFilter, out IOleMessageFilter oldFilter);
}
Я определил возвращаемые значения как константы для лучшей читаемости и полностью отредактировал все это, чтобы избавиться от части дублирования из примера MSDN, поэтому, надеюсь, вы найдете его понятным. extern int CoRegisterMessageFilter
- это наше соединение с неуправляемым кодом фильтра сообщений - вы можете читать ключевое слово extern на MSDN.
Теперь все, что осталось, это код, иллюстрирующий использование:
using System.Runtime.InteropServices;
using EnvDTE;
[STAThread]
public static void Main()
{
MessageFilter.Register();
var process = GetProcess(7532);
if (process != null)
{
process.Attach();
Console.WriteLine("Attached to {0}", process.Name);
}
MessageFilter.Revoke();
Console.ReadLine();
}
private static Process GetProcess(int processID)
{
var dte = (DTE)Marshal.GetActiveObject("VisualStudio.DTE.10.0");
var processes = dte.Debugger.LocalProcesses.OfType<Process>();
return processes.SingleOrDefault(x => x.ProcessID == processID);
}