以编程方式分离调试器

时间:2011-09-20 05:25:04

标签: c# .net debugging

我有一个第三方库在内部执行某些操作会导致它在连接调试器时大大减慢,即使在发布模式下也是如此。

我已经找到了100个关于如何通过转到Debug -> Detach process在Visual Studio中手动分离调试器的解释。但是,我还没有看到任何人提供一个示例,程序可以将任何附加的调试器分离给自己。

基本上是Debugger.Launch()的分离版本吗?

2 个答案:

答案 0 :(得分:6)

根据Why can't you detach in interop-debugging?,CLR不支持分离进程。但Visual Studio可以做到这一点。但是这篇文章已经有5年了,那么可以通过pinvoke从Windows Api中使用DebugActiveProcessStop吗?

BOOL WINAPI DebugActiveProcessStop(
  __in  DWORD dwProcessId
);


[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool DebugActiveProcessStop([In] int Pid );

编辑:试过这个:在当前进程中,即使提升也会拒绝访问。

CLR Managed Debugger (mdbg) Sample 20062011 version

中也有任何内容

最后这篇文章explains what you need to do to use ICorDebug::Detach,我认为visual studio确实这样做了。

答案 1 :(得分:2)

假设您知道Visual Studio的哪个实例/版本附加到您的进程,您可以按如下方式分离它:

object dt = Marshal.GetActiveObject("VisualStudio.DTE.12.0")
DTE dte = (DTE)dt;
dte.Debugger.DetachAll();

" 12"适用于Visual Studio的2013版。对于其他版本,请相应更改。这需要引用EnvDTE,它通常位于C:\ Program Files(x86)\ Microsoft Visual Studio 10.0 \ Common7 \ IDE \ PublicAssemblies \ EnvDTE.dll

这将从Visual Studio的实例中删除所有进程,因此,无论出于何种原因,除了您的Visual Studio之外,Visual Studio还附加到其他进程,这些进程也将分离。此外,如果您打开了多个Visual Studio实例,则可能会收到意外结果。

要小心只分离当前进程和只有正确的Visual Studio实例,请使用以下代码:

using System;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using EnvDTE;

public class Test
{
    public static void DetachCurrentProcesses()
    {
        System.Diagnostics.Process[] procs = System.Diagnostics.Process.GetProcessesByName("devenv");

        DTE dte = null;

        foreach (System.Diagnostics.Process devenv in procs) {
            do {
                System.Threading.Thread.Sleep(2000);
                dte = AutomateVS.GetDTE(devenv.Id);
            } while (dte == null);

            IEnumerable<Process> processes = dte.Debugger.DebuggedProcesses.OfType<Process>();

            if (!processes.Any)
                continue;

            int currentID = System.Diagnostics.Process.GetCurrentProcess().Id;

            processes.Where(p => p.ProcessID == currentID).ToList.ForEach(p => p.Detach(false));

            Marshal.ReleaseComObject(dte);
        }
    }
}

/// <summary>
/// Source to this class:  http://blogs.msdn.com/b/kirillosenkov/archive/2011/08/10/how-to-get-dte-from-visual-studio-process-id.aspx
/// </summary>
/// <remarks></remarks>
public class AutomateVS
{
    [DllImport("ole32.dll")]
    private static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);

    public static DTE GetDTE(int processId)
    {
        string progId = "!VisualStudio.DTE.10.0:" + processId.ToString();
        object runningObject = null;

        IBindCtx bindCtx = null;
        IRunningObjectTable rot = null;
        IEnumMoniker enumMonikers = null;

        try
        {
            Marshal.ThrowExceptionForHR(CreateBindCtx(reserved: 0, ppbc: out bindCtx));
            bindCtx.GetRunningObjectTable(out rot);
            rot.EnumRunning(out enumMonikers);

            IMoniker[] moniker = new IMoniker[1];
            IntPtr numberFetched = IntPtr.Zero;
            while (enumMonikers.Next(1, moniker, numberFetched) == 0)
            {
                IMoniker runningObjectMoniker = moniker[0];

                string name = null;

                try
                {
                    if (runningObjectMoniker != null)
                    {
                        runningObjectMoniker.GetDisplayName(bindCtx, null, out name);
                    }
                }
                catch (UnauthorizedAccessException)
                {
                    // Do nothing, there is something in the ROT that we do not have access to.
                }

                if (!string.IsNullOrEmpty(name) && string.Equals(name, progId, StringComparison.Ordinal))
                {
                    Marshal.ThrowExceptionForHR(rot.GetObject(runningObjectMoniker, out runningObject));
                    break;
                }
            }
        }
        finally
        {
            if (enumMonikers != null)
            {
                Marshal.ReleaseComObject(enumMonikers);
            }

            if (rot != null)
            {
                Marshal.ReleaseComObject(rot);
            }

            if (bindCtx != null)
            {
                Marshal.ReleaseComObject(bindCtx);
            }
        }

        return (DTE)runningObject;
    }

}