Прикрепить отладчик в С# к другому процессу - программирование
Подтвердить что ты не робот

Прикрепить отладчик в С# к другому процессу

Я хотел бы иметь возможность автоматически присоединять отладчик, что-то вроде: System.Diagnostics.Debugger.Launch(), кроме, а не текущего процесса, для другого именованного процесса. У меня есть имя процесса и PID для идентификации другого процесса.

Возможно ли это?

4b9b3361

Ответ 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);
}

Ответ 2

Проверьте это

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using EnvDTE;
using NUnit.Framework;

namespace UnitTests
{
    [TestFixture]
    public class ForTest
    {
        [STAThread]  
        [Test]
        public void Test()
        {
            var dte = (DTE) Marshal.GetActiveObject("VisualStudio.DTE.10.0");
            MessageFilter.Register();

            IEnumerable<Process> processes = dte.Debugger.LocalProcesses.OfType<Process>();
            var process = processes.SingleOrDefault(x => x.ProcessID == 6152);
            if (process != null)
            {
                process.Attach();
            }
        }
    }

    public class MessageFilter : IOleMessageFilter
    {
        //
        // Class containing the IOleMessageFilter
        // thread error-handling functions.

        // Start the filter.

        //
        // IOleMessageFilter functions.
        // Handle incoming thread requests.

        #region IOleMessageFilter Members

        int IOleMessageFilter.HandleInComingCall(int dwCallType,
                                                 IntPtr hTaskCaller, int dwTickCount, IntPtr
                                                                                          lpInterfaceInfo)
        {
            //Return the flag SERVERCALL_ISHANDLED.
            return 0;
        }

        // Thread call was rejected, so try again.
        int IOleMessageFilter.RetryRejectedCall(IntPtr
                                                    hTaskCallee, int dwTickCount, int dwRejectType)
        {
            if (dwRejectType == 2)
            // flag = SERVERCALL_RETRYLATER.
            {
                // Retry the thread call immediately if return >=0 & 
                // <100.
                return 99;
            }
            // Too busy; cancel call.
            return -1;
        }

        int IOleMessageFilter.MessagePending(IntPtr hTaskCallee,
                                             int dwTickCount, int dwPendingType)
        {
            //Return the flag PENDINGMSG_WAITDEFPROCESS.
            return 2;
        }

        #endregion

        public static void Register()
        {
            IOleMessageFilter newFilter = new MessageFilter();
            IOleMessageFilter oldFilter = null;
            CoRegisterMessageFilter(newFilter, out oldFilter);
        }

        // Done with the filter, close it.
        public static void Revoke()
        {
            IOleMessageFilter oldFilter = null;
            CoRegisterMessageFilter(null, out oldFilter);
        }

        // Implement the IOleMessageFilter interface.
        [DllImport("Ole32.dll")]
        private static extern int
            CoRegisterMessageFilter(IOleMessageFilter newFilter, out
                                                                     IOleMessageFilter oldFilter);
    }

    [ComImport, Guid("00000016-0000-0000-C000-000000000046"),
     InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    internal 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);
    }
}

Ответ 3

Возможность запуска; vsjitdebugger.exe -p ProcessId

Возможно, можно использовать Process.Start для этого в приложении С#.

Ответ 4

Простейший способ сделать это.

public static void Attach(DTE2 dte)
        {
            var processes = dte.Debugger.LocalProcesses;
            foreach (var proc in processes.Cast<EnvDTE.Process>().Where(proc => proc.Name.IndexOf("YourProcess.exe") != -1))
                proc.Attach();
        }

        internal static DTE2 GetCurrent()
        {
            var dte2 = (DTE2)Marshal.GetActiveObject("VisualStudio.DTE.12.0"); // For VisualStudio 2013

            return dte2;
        }

Использование:

Attach(GetCurrent());