调试器附件的自动化

时间:2013-04-02 18:18:25

标签: .net visual-studio debugging sdk automation

我有两个项目,一个是“前端”或加载项目,另一个是Web服务,实际上是一个单独的文件夹/解决方案。

我已经在使用我的前端来加载那个项目到Cassini(实际上有两个)。我想做的是将调试器附加到我的两个Web服务及其相关的源代码,在运行时,前端,甚至那些项目,都不包含在前端解决方案中。

我可以访问源代码和pdb,所以我相信我应该能够做到这一点,但不是百分之百确定,并且在网上这样做的信息是......稀疏,充其量。

有没有办法在.NET,Visual Studio SDK或其他第三方框架中执行此操作?

我可以访问VS 2010和VS 2012

2 个答案:

答案 0 :(得分:0)

要调用的VS API为EnvDTE.Process.Attach

API的文档提供了一个按名称附加进程的示例,该示例不适用于多个应用程序池。您可以通过command line tools (may require elevation)w3wp command line按应用程序池名称找到正确的w3wp流程。

答案 1 :(得分:0)

所以你在这里有几个选择。

第一个选项(如果适用)是添加以下行:

#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif

这通常会弹出“Visual Studio JIT调试”窗口,允许您选择正在运行的实例或启动新的Visual Studio。

或者,如果你想要一些......“更棘手”,你可以迭代运行进程,查找任何Visual Studio实例,并使用EnvDTE API绑定它们。这是 FAR 更复杂,所以如果适用,我推荐上面更简单的选项,但是这里有:

首先,我们需要一种“查找”Visual Studio实例的方法:

private static IEnumerable<Process> GetVisualStudioProcesses()
{
    var processes = Process.GetProcesses();
    return processes.Where(o => o.ProcessName.Contains("devenv"));
}

然后我们可以使用它来尝试连接到运行实例的正确的基于COM的接口:

private static bool TryGetVsInstance(int processId, out EnvDTE._DTE instance)
{
    var numFetched = IntPtr.Zero;
    var monikers = new IMoniker[1];
    IRunningObjectTable runningObjectTable;
    IEnumMoniker monikerEnumerator;

    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 EnvDTE._DTE && runningObjectName.StartsWith("!VisualStudio"))
        {                    
            var currentProcessId = int.Parse(runningObjectName.Split(':')[1]);
            if (currentProcessId == processId)
            {
                instance = (EnvDTE._DTE)runningObjectVal;
                return true;
            }
        }
    }

    instance = null;
    return false;
}

COM定义是:

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

我们可以将其包含在“尝试附加到”调用中,如下所示:

public static bool AttachToProcess(int processId)
{
#if !DEBUG
    // Not allowed if not in DEBUG mode...
    return false;
#else
    MessageFilter.Register();
    Exception lastException = null;
    bool attachSuccess = false;
    while (!attachSuccess && !(lastException is COMException))
    {
        Log("Attempting DynamicAttach to process id:" + processId);
        try
        {
            Log("Querying for active VS instances...");
            var studioProcesses = GetVisualStudioProcesses();
            var studioDtes = studioProcesses
                .Select(proc =>
                {
                    EnvDTE._DTE dte;
                    if (TryGetVsInstance(proc.Id, out dte))
                    {
                        return new DteWrapper(dte);
                    }
                    return null;
                })
                .Where(dte => dte != null);
            foreach (var studioDte in studioDtes)
            {
                attachSuccess = TryAttachTo(processId, studioDte, out lastException);
                if(attachSuccess) break;
            }
        }
        catch (Exception vsex)
        {
            lastException = vsex;
        }

    }
    MessageFilter.Revoke();
    return attachSuccess;
#endif
}

private static bool TryAttachTo(int processId, DteWrapper studioDte, out Exception lastException)
{
    bool attachSuccess = false;
    lastException = null;
    Log("Attempting to attach to process " + processId);
    try
    {
        var localProcesses =
            from localProcess in studioDte.Debugger.LocalProcesses.Cast<EnvDTE.Process>()
            let processWrapper = new ProcessWrapper(localProcess)
            where processWrapper.Process2 == null || !processWrapper.Process2.IsBeingDebugged
            select processWrapper;
        if (!localProcesses.Any())
        {
            lastException = new ApplicationException("Can not find process to attach to!");
            return false;
        }
        foreach (var process in localProcesses)
        {
            try
            {
                if (process.Process.ProcessID == processId)
                {
                    Log("Found dte process to attach to, attempting attach...", studioDte);
                    try
                    {
                        process.Process.Attach();
                        Log("Attached to process!");
                        attachSuccess = true;
                        AtLeastOneDebuggerWasAttached = true;
                    }
                    catch (Exception detachException)
                    {
                        Log("Could not attach to process:" + detachException, studioDte);
                        attachSuccess = false;
                        lastException = detachException;
                    }
                }
            }
            catch (Exception attachException)
            {
                lastException = attachException;
            }
        }
    }
    catch (Exception queryProcessesException)
    {
        lastException = queryProcessesException;
    }
    return attachSuccess;
}

将一些辅助类定义为:

internal class DteWrapper
{
    public DteWrapper(EnvDTE._DTE dte)
    {
        Dte = dte as EnvDTE.DTE;
        Dte2 = dte as EnvDTE80.DTE2;
        Debugger = dte.Debugger;
        Debugger2 = (Dte2 != null) ? Dte2.Debugger as EnvDTE80.Debugger2 : null;
        Events = dte.Events;
        DebuggerEvents = dte.Events.DebuggerEvents;
    }

    public EnvDTE.DTE Dte { get; private set; }
    public EnvDTE80.DTE2 Dte2 { get; private set; }

    public EnvDTE.Debugger Debugger { get; private set; }
    public EnvDTE80.Debugger2 Debugger2 { get; private set; }

    public EnvDTE.Events Events { get; private set; }
    public EnvDTE.DebuggerEvents DebuggerEvents { get; private set; }
}
internal class ProcessWrapper
{
    public ProcessWrapper(EnvDTE.Process dteProcess)
    {
        this.Process = dteProcess;
        this.Process2 = dteProcess as EnvDTE80.Process2;
        this.Process3 = dteProcess as EnvDTE90.Process3;
        this.ManagedProcess = System.Diagnostics.Process.GetProcessById(dteProcess.ProcessID);
    }
    public Process ManagedProcess { get; private set; }
    public EnvDTE.Process Process { get; private set; }
    public EnvDTE80.Process2 Process2 { get; private set; }
    public EnvDTE90.Process3 Process3 { get; private set; }
}