C#:附加调试器以干净的方式处理 [英] C#: Attach debugger to process in a clean way

查看:158
本文介绍了C#:附加调试器以干净的方式处理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

We are developing an open source Visual Studio extension for running tests written with the C++ Google Test framework within VS. Part of the VS API for test adapters is the possibility to run tests with a debugger attached. However, that API does not allow to grab the output of the executing process: It only returns the process id, and afaik, there's no way to access that output if the process is already running.

因此,我们希望启动我们自己的进程,并将调试器附加到该进程(按照这个问题)。这是迄今为止的工作,但是我们有一个问题:似乎只有在进程已经运行的情况下才能附加调试器,导致错误的断点;原因似乎是断点可能已经被传递,直到调试器被附加。请注意,我们确实触及了断点,所以这种方法似乎总体上起作用,但并不完全可靠。

Thus, we'd like to launch our own process, and attach a debugger to that process on our own (following the approach described in the accepted answer of this question). This works so far, but we have one issue: It seems that attaching a debugger is only possible if the process already runs, resulting in missed breakpoints; the reason seems to be that the breakpoints might already be passed until the debugger is attached. Note that we indeed hit breakpoints, so the approach seems to work in general, but it's not exactly reliable.

以下是启动流程的代码(其中命令是由Google Test框架生成的可执行文件):

Here's the code for launching the process (where command is the executable produced by the Google Test framework):

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

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

And here's the utility method for attaching the debugger:

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()方法中添加了1秒的等待时间来测试测试可执行文件,但这不是一个选择,因为我们的用户需要更改他们的测试代码,以便能够使用我们的扩展进行调试)...或者甚至有一个干净的方式(描述的方式可能会明显失败,例如缓慢的机器)?

Is there any way to attach a debugger in a more reliable manner? For instance, is it possible to launch a process from C# such that the process will wait for, say, 1s before starting to execute the passed command? That would give us enough time to attach the debugger (at least on my machine - I've tested this by adding a 1s wait period at the main() method of the Google Test executable, but that's not an option, since our users would need to change their test code in order to be able to debug it with our extension)... Or is there even a clean way (the described way might obviously fail e.g. on slow machines)?

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

Update: Let's recap the problem statement: Our users have a C++ solution including tests written with the Google Test framework (which are compiled into an executable to be run e.g. from the command line). We provide a VS extension written in C# (a test adapter) which discovers the executable, runs it with the help of a Process, collects the test results, and displays them within the VS test explorer. Now, if our users click Debug tests, we are starting the process running the C++ executable and then attach a debugger to that process. However, by the time it takes to attach the debugger to the process, the executable has already started running, and some tests have already been executed, resulting in breakpoints within those tests being missed.

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

Since we do not want to force our users to change their C++ code (e.g. by adding some waiting period at the begin of main() method of the test code, or with one of the approaches referenced by Hans below), we need a different way to attach that debugger. In fact, the VS test framework allows to launch a process with a debugger attached (and that approach does not suffer from our problem - that's what we are doing now), but that approach does not allow grabbing the process' output since all we get is the process id of an already running process (at least I don't know how that can be done in this case - I have done my research on that (so I believe :-) ). Grabbing the output would have some significant benefits to our extension (which I do not list here - let me know in the comments if you are interested), so we are looking for a different way to handle such situations.

那么我们如何运行可执行文件(包括抓取可执行文件的输出)和立即附加一个调试器,这样就不会错过任何断点?这是可能的吗?

So how can we run the executable (including grabbing the executable's output) and immediately attach a debugger to it, such that no breakpoints are missed? Is that possible at all?

推荐答案

您可以通过PInvoke CreateProcess(参见示例如何调用CreateProcess()... )启动您的调试程序使用CREATE_SUSPENDED创建标志(请参阅创建标志获取更多详细信息),然后PInvoke ResumeThread在调试器附加后继续。

You can PInvoke CreateProcess (see example How to call CreateProcess()...) to launch your debuggee using the CREATE_SUSPENDED creation flag (see Creation Flags for more detail) and then PInvoke ResumeThread to continue once your debugger is attached.

您可能需要将选项调整为CreateProcess,具体取决于您的具体需求,但应该这样做。

You may need to tweak the options to CreateProcess, depending on your specific needs, but that should do it.

更新
更好的选择,由于您正在编写一个VS扩展,是使用IVsDebugger4接口调用 LaunchDebugTargets4 。该界面已被记录在案,您可以在GitHub上找到很多示例(仅搜索LaunchDebugTargets4)。这种方法将避免VS附带使用本机调试引擎的麻烦中断。

Update: A much better option, since you are writing a VS extension, is to use IVsDebugger4 interface to call LaunchDebugTargets4. The interface is documented and you can find plenty of examples on GitHub (just search for LaunchDebugTargets4). This method will avoid that pesky break in VS once you attach with the native debug engine.

这篇关于C#:附加调试器以干净的方式处理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆