【问题标题】:try/catch with __debugbreak()使用 __debugbreak() 尝试/捕获
【发布时间】:2016-01-05 13:19:35
【问题描述】:

我正在使用在某些情况下运行 __debugbreak() 的第 3 方 C++ DLL,并且在这样做之前没有检查 IsDebuggerPresent()。当这种情况发生在调试器之外(例如,运行应用程序的最终用户)时,这会导致我的应用程序“崩溃”。我想自己抓住它并处理它,或者至少忽略它。

我实际上已经有一段时间使用未处理的异常过滤器将 SEH 转换为 C++ 异常,所以它不起作用有点奇怪。

::SetUnhandledExceptionFilter(OnUnhandledException);

我一直在做一些直接测试,标准的 __try/__except 有效,所以我可以将每个调用包装到 DLL 中,以此作为后备,但似乎如果 __try/__except 有效,那么 ::SetUnhandledExceptionFilter () 也应该可以工作。

    __try
    {
        __debugbreak();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
        printf("caught");
    }

try/catch(...) 不起作用。

    try
    {
        __debugbreak();
    }
    catch (...)
    {
        printf("caught");
    }

_set_se_translator() 也不起作用。

来自https://msdn.microsoft.com/en-us/library/ms679297(VS.85).aspx 的 MSDN 文档,它声明它应该作为结构化异常运行。我意识到这是 DebugBreak() 的文档,但我也对此进行了测试,并且遇到了同样的问题,即使是“catch(...)”。

我正在使用 /EHa 进行编译。

如何捕捉 __debugbreak (asm INT 3),或者至少改变行为?

【问题讨论】:

  • 我已经尝试过 (SetErrorMode, SetUnhandledExceptionFilter),它适用于访问冲突等问题,但不适用于 __debugbreak() (int 3)。到目前为止,我发现的唯一可以捕捉到的是明确的 __try/__except。我觉得我缺少一些设置。

标签: c++ winapi exception exception-handling


【解决方案1】:

断点生成EXCEPTION_BREAKPOINT 结构化异常。您不能使用 try/catch 来捕获它,因为它不会被转换为 C++ 异常,与 /EHa 开关或 _set_se_translator 无关。 EXCEPTION_BREAKPOINT 是一个特殊的例外。

首先,您应该知道 catch 块和 __except 块仅在展开堆栈后才会执行。这意味着在处理程序块之后继续执行,而不是在调用__debugbreak() 之后继续执行。因此,如果您只想跳过EXCEPTION_BREAKPOINT,同时在int 3 指令之后继续执行。您应该使用向量异常处理程序。这是一个例子:

// VEH is supported only on Windows XP+ and Windows Server 2003+
#define _WIN32_WINNT 0x05020000

#include <windows.h>
#include <stdio.h>

//AddVectoredExceptionHandler constants:
//CALL_FIRST means call this exception handler first;
//CALL_LAST means call this exception handler last
#define CALL_FIRST 1  
#define CALL_LAST 0

LONG WINAPI
VectoredHandlerBreakPoint(
struct _EXCEPTION_POINTERS *ExceptionInfo
    )
{
    if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT)
    {
        /*

        If a debugger is attached, this will never be executed.

        */

        printf("BreakPoint at 0x%x skipped.\n", ExceptionInfo->ExceptionRecord->ExceptionAddress);

        PCONTEXT Context = ExceptionInfo->ContextRecord;

        // The breakpoint instruction is 0xCC (int 3), just one byte in size.
        // Advance to the next instruction. Otherwise, this handler will just be called ad infinitum.
#ifdef _AMD64_
        Context->Rip++;
#else
        Context->Eip++;
#endif    
        // Continue execution from the instruction at Context->Rip/Eip.
        return EXCEPTION_CONTINUE_EXECUTION;
    }

    // IT's not a break intruction. Continue searching for an exception handler.
    return EXCEPTION_CONTINUE_SEARCH;
}

void main()
{
    // Register the vectored exception handler once.
    PVOID hVeh = AddVectoredExceptionHandler(CALL_FIRST, VectoredHandlerBreakPoint);

    if (!hVeh)
    {
        // AddVectoredExceptionHandler failed.
        // Practically, this never happens.
    }

    DebugBreak();

    // Unregister the handler.
    if (hVeh)
        RemoveVectoredExceptionHandler(hVeh);
}

这样,断点指令int 3就会被跳过,执行下一条指令。此外,如果附加了调试器,它将为您处理 EXCEPTION_BREAKPOINT

但是,如果你真的想展开堆栈,你必须使用__except(GetExceptionCode() == EXCEPTION_BREAKPOINT ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)

【讨论】:

  • 非常有帮助 - 谢谢。我最终只是在特定调用周围使用了 __except(),但很高兴看到有一种方法可以处理它们。也有助于了解如何跳过它。
猜你喜欢
  • 2023-03-07
  • 1970-01-01
  • 1970-01-01
  • 2019-08-25
  • 2017-04-14
  • 2019-08-02
  • 1970-01-01
  • 1970-01-01
  • 2017-09-12
相关资源
最近更新 更多