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

C#: Attach debugger to process in a clean way

我们正在 VS 中开发开源 Visual Studio extension for running tests written with the C++ Google Test 框架。用于测试适配器的 VS API 的一部分是 运行 附加调试器进行测试的可能性。但是,API 不允许获取正在执行的进程的输出:它只有 returns 进程 ID,而且 afaik,如果进程已经 运行,则无法访问该输出]宁.

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

这是启动进程的代码(其中 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 测试可执行文件的 main() 方法中添加 1s 等待时间来测试它,但这不是选项,因为我们的用户需要更改他们的测试代码以便能够使用我们的扩展对其进行调试)...或者甚至有一种干净的方法(所描述的方法可能会明显失败,例如在慢速机器上)?

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

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

那么我们怎样才能 运行 可执行文件(包括抓取可执行文件的输出)并 立即 将调试器附加到它,这样就不会遗漏任何断点?这可能吗?

您可以 PInvoke CreateProcess(有关更多详细信息,请参见示例 How to call CreateProcess()...) to launch your debuggee using the CREATE_SUSPENDED creation flag (see Creation Flags),然后 PInvoke ResumeThread 以在附加调试器后继续。

您可能需要调整 CreateProcess 的选项,具体取决于您的具体需要,但应该可以做到。

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