非托管代码中 System.Diagnostics.Debugger.Launch() 的等价物是啥?

Posted

技术标签:

【中文标题】非托管代码中 System.Diagnostics.Debugger.Launch() 的等价物是啥?【英文标题】:What is the equivalent of System.Diagnostics.Debugger.Launch() in unmanaged code?非托管代码中 System.Diagnostics.Debugger.Launch() 的等价物是什么? 【发布时间】:2013-12-02 21:08:44 【问题描述】:

当满足某些条件时,我需要从我的本机 C++ 程序启动调试器。在 C# 中,我只调用 System.Diagnostics.Debugger.Launch()。我认为 Win32 DebugBreak() 调用会做我想做的事,但如果没有调试器,它只会终止应用程序。

如何从本机代码启动调试器的新实例(著名的“可能的调试器”对话框)?甚至可能吗?我可以尝试使用 COM 来创建一个新的 Visual Studio 实例,但它有点复杂,而且还会将我锁定到特定版本的 VS。

【问题讨论】:

我也遇到了同样的问题,也没有找到解决办法。我终于解决了我的问题,方法是将 System.Diagnostics.Debugger.Launch 调用放在混合模式 DLL 中,将该函数导出为非托管函数,然后使用 LoadLibrary 从我的非托管应用程序中显式加载库。 这很酷,但当您的应用程序托管 CLR 时就不行了。加载托管库有点搞砸了整个托管的事情 【参考方案1】:

我发现可以直接用当前进程的PID调用vsjitdebugger.exe。确保在 Visual Studio 中的 Tools->Options->Debugging->Just-in-Time 中选择了“Native”。

这是启动调试器的 C++ 代码。它使用各种 Win32 API 的 UNICODE 版本。我得到系统目录,因为 CreateProcess() 不使用 PATH。

bool launchDebugger()

    // Get System directory, typically c:\windows\system32
    std::wstring systemDir(MAX_PATH+1, '\0');
    UINT nChars = GetSystemDirectoryW(&systemDir[0], systemDir.length());
    if (nChars == 0) return false; // failed to get system directory
    systemDir.resize(nChars);

    // Get process ID and create the command line
    DWORD pid = GetCurrentProcessId();
    std::wostringstream s;
    s << systemDir << L"\\vsjitdebugger.exe -p " << pid;
    std::wstring cmdLine = s.str();

    // Start debugger process
    STARTUPINFOW si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);

    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

    if (!CreateProcessW(NULL, &cmdLine[0], NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)) return false;

    // Close debugger process handles to eliminate resource leak
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);

    // Wait for the debugger to attach
    while (!IsDebuggerPresent()) Sleep(100);

    // Stop execution so the debugger can take over
    DebugBreak();
    return true;

【讨论】:

太棒了!这正是我调试进程外 COM 服务器所需要的。 我目前正在尝试在托管应用程序中托管 CoreCLR,我发现这是让混合调试正常工作的唯一方法。从 VS 混合调试开始会导致调试器崩溃,仅调用 DebugBreak 会导致死锁,System.Diagnostics.Debugger.Launch 也是如此。 经过长时间的挣扎,这救了我。我还必须在调试器设置中设置“Native”,并将工作目录和输出设置到创建进程的应用程序(通过 QProcess)。 不错!我想当用户在 vsjitdebugger 对话框上单击“取消”时,没有什么好办法不永远等待吗?无需为要附加的调试器引入竞争。我想我们能做的最好的事情就是等待进程退出,然后在调试器附加自身时稍等片刻(例如超时 1 分钟)? 回答我的最后一条评论:您可以等待进程以 exit-code 0 退出,然后等待调试器附加。我找不到任何文档,但是当用户单击取消或 X 关闭对话框时,测试退出代码为 262320。【参考方案2】:

DebugBreak() 很好,__debugbreak() 内在函数也很好。他们都做同样的事情,他们用 STATUS_BREAKPOINT 异常使程序崩溃。然后触发 Windows 错误报告对话框,它会滚动一段时间,然后提供调试按钮。然后启动调试器。

您可能犯的唯一真正错误是等待 WER 对话框的时间不够长,并且太快按下取消。或者禁用 WER。如果根本没有可用的调试器,是的,你不能选择一个。

重要的注册表项是 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug\Debugger。通常设置为 vsjitdebugger.exe,即显示“可能的调试器”对话框的那个。

【讨论】:

不。 DebugBreak() 导致错误对话框“foobar.exe 停止工作,Windows 正在检查问题的解决方案”。在任何时候,我都无法选择调试器。至少这是在 Windows 7 上发生的情况。 这就是我提到的 WER 对话框。点击你的脚,不要点击取消。并检查该注册表项。 确实,如果您等待一段时间然后单击“调试”,您就可以选择一个调试器。应该更有耐心。但是,System.Diagnostics.Debugger.Launch() 会立即进入调试器选择对话框,无需等待 45 秒。另一个复杂因素是我的应用程序没有被用户直接调用。如果是这样,我不需要技巧来附加调试器。如果我使用 DebugBreak(),父应用程序似乎认为子进程刚刚死掉并放弃了我,但我还没有深入调查。 叹息,你为什么在你的问题中忽略了这些重要的细节?使用this approach instead。 谢谢。我知道这个。另一个重要的细节 :) 是启动了多个同名进程。我不想调试所有这些,只有在满足某些条件时才需要启动调试器。我听说过一种解决方法,它建议创建一个设置了图像文件执行选项的 DLL,并在需要启动调试器时加载它。说了这么多,仅启动调试器选择对话框的能力将非常有帮助。毕竟,.NET 以某种方式做到了,所以它一定是可能的。【参考方案3】:

从您的 Visual C++ 代码中调用_CrtDbgBreak(),重新编译,运行您的程序,然后从对话框中选择调试程序

【讨论】:

以上是关于非托管代码中 System.Diagnostics.Debugger.Launch() 的等价物是啥?的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio调试托管代码

等效于 System.Diagnostics.StackTrace 的 C# 可移植类库

如何将 System.Diagnostics.Trace 和 System.Diagnostics.Debug 消息记录到 NLog 文件?

非控制台应用程序输出信息到输出面板

如何从 System.Diagnostics.Process 停止进程并最终获取统计信息

System.Diagnostics.Process 启动进程资源或调用外部的命令的使用