C#:以干净的方式将调试器附加到进程

Posted

技术标签:

【中文标题】C#:以干净的方式将调试器附加到进程【英文标题】:C#: Attach debugger to process in a clean way 【发布时间】:2016-08-11 06:36:48 【问题描述】:

我们正在开发一个开源 Visual Studio extension,用于在 VS 中运行使用 C++ Google Test 框架编写的测试。用于测试适配器的 VS API 的一部分是可以使用附加的调试器运行测试。但是,该 API 不允许获取正在执行的进程的输出:它只返回进程 ID,而且如果进程已经在运行,则无法访问该输出。

因此,我们希望启动我们自己的进程,并自行将调试器附加到该进程(遵循this question 的已接受答案中描述的方法)。到目前为止这是可行的,但我们有一个问题:似乎只有在进程已经运行时才能附加调试器,从而导致错过断点;原因似乎是断点可能已经传递,直到附加调试器。请注意,我们确实遇到了断点,因此该方法似乎总体上可行,但并不完全可靠。

这是启动进程的代码(其中command 是由 Google 测试框架生成的可执行文件):

var processStartInfo = new ProcessStartInfo(command, param)

    RedirectStandardOutput = true,
    RedirectStandardError = false,
    UseShellExecute = false,
    CreateNoWindow = true,
    WorkingDirectory = workingDirectory
;

Process process = new Process  StartInfo = processStartInfo ;
process.Start()

DebuggerAttacher.AttachVisualStudioToProcess(vsProcess, vsInstance, process);

这是附加调试器的实用方法:

internal static void AttachVisualStudioToProcess(Process visualStudioProcess, _DTE visualStudioInstance, Process applicationProcess)

    //Find the process you want the VS instance to attach to...
    DTEProcess processToAttachTo = visualStudioInstance.Debugger.LocalProcesses.Cast<DTEProcess>().FirstOrDefault(process => process.ProcessID == applicationProcess.Id);

    //AttachDebugger to the process.
    if (processToAttachTo != null)
    
        processToAttachTo.Attach();

        ShowWindow((int)visualStudioProcess.MainWindowHandle, 3);
        SetForegroundWindow(visualStudioProcess.MainWindowHandle);
    
    else
    
        throw new InvalidOperationException("Visual Studio process cannot find specified application '" + applicationProcess.Id + "'");
    

有什么方法可以更可靠地附加调试器吗?例如,是否可以从 C# 启动一个进程,使该进程在开始执行传递的命令之前等待,比如说 1 秒?这会给我们足够的时间来附加调试器(至少在我的机器上 - 我已经通过在 Google Test 可执行文件的 main() 方法中添加 1 秒等待期来测试这个,但这不是一个选项,因为我们的用户会需要更改他们的测试代码以便能够使用我们的扩展对其进行调试)......或者甚至有一种干净的方式(所描述的方式可能显然会失败,例如在慢速机器上)?

更新:让我们回顾一下问题陈述:我们的用户有一个 C++ 解决方案,包括使用 Google 测试框架编写的测试(这些测试被编译成可执行文件,以便例如从命令行运行)。我们提供了一个用 C#(测试适配器)编写的 VS 扩展,它发现可执行文件,在 Process 的帮助下运行它,收集测试结果,并在 VS 测试资源管理器中显示它们。现在,如果我们的用户单击调试测试,我们将启动运行 C++ 可执行文件的进程,然后将调试器附加到该进程。但是,在将调试器附加到进程所需的时间时,可执行文件已经开始运行,并且一些测试已经执行,导致这些测试中的断点被遗漏。

由于我们不想强迫我们的用户更改他们的 C++ 代码(例如,通过在测试代码的 main() 方法的开头添加一些等待时间,或者使用下面 Hans 引用的方法之一),我们需要一种不同的方式来附加该调试器。事实上,VS 测试框架允许启动一个附加了调试器的进程(并且这种方法不会遇到我们的问题 - 这就是我们现在正在做的),但是这种方法不允许抓取进程的输出,因为我们得到的只是是一个已经运行的进程的进程ID(至少我不知道在这种情况下如何做到这一点 - 我已经对此进行了研究(所以我相信:-))。获取输出将对我们的扩展有一些显着的好处(我没有在这里列出 - 如果您有兴趣,请在 cmets 中告诉我),因此我们正在寻找一种不同的方式来处理这种情况。

那么我们如何运行可执行文件(包括获取可执行文件的输出)并立即为其附加调试器,这样就不会遗漏断点?这可能吗?

【问题讨论】:

***.com/a/3575068/17034 我不明白您参考中提到的内容将如何解决我们的问题。 Mutex 和 __debugbreak() 将需要修改被测代码,这不是选项,正如我所说的(尽管比在被测代码中等待固定时间更干净)。我怀疑我是否可以比我们的代码更快地附加调试器:-)(您答案的选项 1),即使我可以,这也会给我们的用户带来很大的负担。您的其他选择根本不适合我们的问题。还是我错过了什么?我的问题描述是否不够精确? @csoltenborn,使用 Debugger.IsAttached 属性怎么样?它可以帮助我们检查调试器是否附加到一个进程,例如,如果您需要在附加到它之前运行其他操作,您可以先停止它。或者使用 Condition 断点来处理丢失的断点命中作为解决方法:msdn.microsoft.com/en-us/library/7sye83ce(v=vs.100).aspx @JackZhai 谢谢你的建议,但我还是不明白这对我们有什么帮助。需要在要调试的进程上调用Debugger.IsAttached,这是我无法更改的本机.exe。我知道条件断点,但我看不出它们有什么帮助......我怎样才能停止正在运行的进程(除非已经附加了调试器)?看起来我需要改进我的问题描述......如果我遗漏了什么,请告诉我(也许在阅读我改进的问题陈述之后)! 你有没有想过 PInvoking CreateProcess 并通过创建标志来启动进程暂停? 【参考方案1】:

您可以 PInvoke CreateProcess(参见示例 How to call CreateProcess()...)使用 CREATE_SUSPENDED 创建标志(参见 Creation Flags 了解更多详细信息)启动调试对象,然后在附加调试器后 PInvoke ResumeThread 继续。

您可能需要根据您的具体需求调整 CreateProcess 的选项,但应该这样做。

更新很多更好的选择,因为您正在编写一个 VS 扩展,是使用 IVsDebugger4 接口调用LaunchDebugTargets4。该界面已记录在案,您可以在 GitHub 上找到大量示例(只需搜索 LaunchDebugTargets4)。一旦你附加了原生调试引擎,这种方法将避免 VS 中令人讨厌的中断。

【讨论】:

工作正常,再次感谢。还有一个问题(我们可能可以解决):在触发第一个断点之前,或者即使没有设置断点,VS 也会显示“exe 已触发断点”窗口(很可能是因为在我们附加VS 调试器)。一旦我们确定这是可行的方法,我会尽快接受您的回答。

以上是关于C#:以干净的方式将调试器附加到进程的主要内容,如果未能解决你的问题,请参考以下文章

暂停进程以留出时间让调试器附加

ABP框架 - 介绍 VS2017调试器无法附加到IIS进程(w3wp.exe) c# 动态实例化一个泛型类

无法将 matlab.exe 进程附加到 Visual Studio 2013 以调试 mex 文件?

使用多个引擎将 Visual Studio 2013 附加到托管和 C++ 进程

使用 Debugger.Break() 将调试器附加到 Vista 或 Windows 7 上正在运行的进程

为啥要等待调试器,然后将调试器附加到进程?