【问题标题】:Attach debugger in C# to another process将 C# 中的调试器附加到另一个进程
【发布时间】:2012-08-04 20:39:00
【问题描述】:

我希望能够自动附加一个调试器,例如:System.Diagnostics.Debugger.Launch(),除了当前进程到另一个命名进程之外。我有一个进程名称和 PID 来识别另一个进程。

这可能吗?

【问题讨论】:

  • @GSerjo 是 Visual Studio 宏吗?什么是 DTE?
  • 是的,它是宏。 DTE 对象代表 Visual Studio 集成开发环境 (IDE),是自动化模型层次结构中的最高级别对象

标签: c# debugging


【解决方案1】:

编辑: GSerjo 提供了正确的解决方案。我想分享一些关于如何改进它的想法(和解释)。我希望我改进的答案对遇到同样问题的其他人有用。


将 VS 调试器附加到进程

手动

  1. 打开 Windows 任务管理器(Ctrl + Shift + Esc)。
  2. 转到标签Processes
  3. 右键单击该进程。
  4. 选择Debug

或者,在 Visual Studio 中,选择 Debug > Attach to Process...

根据您是否有权访问源代码,结果会有所不同。

使用 C# 自动生成

注意事项:以下代码很脆弱,因为某些值, 例如 Visual Studio 版本号,是硬编码的。请牢记这一点 如果您打算分发您的程序。

首先,在您的项目中添加对EnvDTE的引用(在解决方案资源管理器中右键单击引用文件夹,添加引用)。在下面的代码中,我只会展示不寻常的 using 指令;像using System 这样的普通的被省略了。

因为您是interacting with COM,所以您需要确保使用STAThreadAttribute 装饰您的Main 方法(应用程序的入口点)。

然后,您需要define the 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 是我们连接到非托管消息过滤器的代码——你可以read up on the extern keyword at 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);
}

【讨论】:

  • 不,这不是我需要的。就像我说的,除了另一个进程之外,我想要一个等价的 Debugger.Launch。
  • 他似乎想以编程方式进行。
  • @DaveHillier C# 和普通的任务管理器有什么区别?
  • @ColeJohnson:我有许多服务要管理。能够部署然后从管理应用程序进行调试对我很有用。我已经有了要安装、停止和启动的服务。
  • 工作得非常好 - 为 VS2012 将 ProgId 提高到 11
【解决方案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);
    }
}

【讨论】:

  • STAThread Attribute 在除入口点(可能是Main)之外的所有方法上都是无用
  • Duuuuude。你在哪里:)
  • @Emad,永远在这里,即使我在那里,我仍然在这里 :)
【解决方案3】:

更简单的方法。

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());

【讨论】:

  • 从哪里获得 DTE2 和 EnvDTE 定义?
  • 您需要添加对 EnvDTE80 的引用(引用 > 添加引用 > 程序集 > 框架),然后在您的类中使用它using EnvDTE80;
  • 显然,它需要 EnvDTE80 和 EnvDTE,它们位于 References > Add references > Assemblies > Extensions(至少在 VS2017 中)。不确定要使用哪个版本号,因为“VisualStudio.DTE.14.0”失败并出现Invalid class string 异常。
  • 我正在使用 VS2013,所以对此无能为力,但 post 可能会有所帮助。祝你好运。
  • 所以它是 15.0,而不是 14.0。像魅力一样工作。
【解决方案4】:

一个选项是运行; vsjitdebugger.exe -p ProcessId

可以使用Process.Start 在 c# 应用程序中执行此操作。

【讨论】:

  • 这会运行一个新的 Visual Studio 实例。
  • 您可以选择现有实例或使用新实例
【解决方案5】:

如果您在将调试器附加到手动附加太快的进程时遇到问题,请不要忘记您有时可以将Console.ReadKey(); 添加到代码的第一行,然后您就可以随时附加它手动。令人惊讶的是,我花了一段时间才弄清楚:D

【讨论】:

    猜你喜欢
    • 2019-01-31
    • 2019-08-12
    • 2011-08-31
    • 2012-09-15
    • 2011-12-07
    • 2011-01-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多