【问题标题】:Catching unhandled exceptions捕获未处理的异常
【发布时间】:2017-11-25 23:11:58
【问题描述】:

在 Windows 中,我有一些线程。其中两个以异常终止(例如,空指针取消引用)。我有SetUnhandledExceptionFilter(...),它在第一个异常时开始生成转储。在第二个异常中,整个程序都死掉了。有没有办法处理这种情况?应忽略除第一个以外的所有严重错误。

伪代码:

  void job()
  {
   ...
   RaiseException(someCode, someFlags, 0, nullptr); // or doing something wrong, like nullptr dereference
  }

  int main() {
    SetUnhandledExceptionFilter(getDump);
    std::thread t1(job), t2(job); 
    ...
  }

UPD:替换误解的字符串 *nullptr = 0xbad;

UPD2:忘记 nullptr

UPD3:到目前为止,我找到了这个解决方法

#include <stdio.h>
#include <windows.h>  // for EXCEPTION_ACCESS_VIOLATION
#include <excpt.h>

#include <mutex>


LONG __stdcall HandleException(EXCEPTION_POINTERS* exinfo)
{
    static HANDLE mutex = CreateMutex(nullptr, FALSE, __TEXT("HandleException"));
    while(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0);

    HANDLE event = CreateEvent(nullptr, TRUE, FALSE, __TEXT("Doing Dump"));
    puts("Doing Dump");
    WaitForSingleObject(event, 5000); // do dump routine
    puts("Done Dump");

    return EXCEPTION_EXECUTE_HANDLER;
}

int filter(unsigned int code, struct _EXCEPTION_POINTERS *ep)
{
    puts("in filter.");
    return HandleException(ep);
}

void Job()
{
    puts("hello");
    int *p = 0x00000000;  // pointer to NULL
    *p = 13;  // causes an access violation exception;
}

void safeJob(void (*job)())
{
    __try {
         job();
    } __except (filter(GetExceptionCode(), GetExceptionInformation())) {
        exit(-1);
    }
}
int main()
{

    SetUnhandledExceptionFilter(HandleException);
    std::thread t1(std::bind(safeJob, Job));
    std::thread t2(std::bind(safeJob, Job));
    t1.join();
    t2.join();
    return 0;
}

【问题讨论】:

  • 听起来像是getDump 中的一个错误。请出示您的实际代码。
  • nullptr 取消引用不一定会引发异常,这是未定义的行为。
  • 我不知道取消引用nullptr 会引发异常的任何系统。它要么因段错误而崩溃,要么什么都不做(尽管它可以做任何事情,因为它是 UB)。
  • 取消引用 std::nullptr_t 是一个编译错误,应该类似于 *(int*)nullptr = 0xbad
  • @Kevin:那个系统叫做“Windows”。 winapi 标签应该已经暗示了这一点。实际上,这个系统被称为 Intel x86。您在英特尔(和兼容设备)上所说的“段错误”是 CPU 生成 exception(如果我没记错的话是 GPF),而 Windows 实际上保留了该术语。

标签: c++ winapi exception-handling seh


【解决方案1】:

根据 Remy 的评论,访问冲突是 Windows“结构化异常”,而不是 C++ 异常,可以使用 Microsoft 特定的扩展(例如 try-except 语句)来处理。

https://msdn.microsoft.com/en-us/library/s58ftw19.aspx

例子:

__try
{
 // doing something wrong
 *nullptr = 0xbad;
}
__except(EXCEPTION_EXECUTE_HANDLER)
{
  // exception quashed!
}

【讨论】:

  • 您能对此答案添加解释吗?
  • 在 Windows 上,取消引用 NULL 指针会引发 SEH EXCEPTION_ACCESS_VIOLATION 异常,而不是 C++ 异常。 __try/__except 可用于处理 SEH 异常。有关详细信息,请参阅 MSDN 上的 Structured Exception Handling
【解决方案2】:

有一种误解,认为“做坏事”会抛出异常。 这是不正确的。如果您正在做的事情会导致未定义的行为,则不应期望任何可预测的行为。如果您的平台抛出异常,那很好,但请意识到您不能指望它。

最好的办法是避免编写此类代码。

有关未定义行为的更多信息,请访问Undefined, unspecified and implementation-defined behavior

【讨论】:

  • 一般来说,这是一个很好的建议,但它并不真正适用于 OPs 案例,因为他已经知道他遇到了异常并且正在尝试排除故障问题。
  • 是的,完全正确。由访问零大小容器引起的空指针取消引用,而不是明确地这样做
  • 如果程序取消引用 nullptr,编程语言 (C++) 不保证会发生什么。另一方面,平台(Windows)可以。它可以预见地引发访问冲突异常。
  • @IInspectable,是的,但正如我希望您已经知道的那样,有一个问题:保证仅在取消引用实际发生时才适用。编译器仍然有权生成发生其他事情的代码 - 我相信最常见的变体是将发生空取消引用的代码部分视为死分支并完全删除。 (当然,不适用于这种特定情况。)
  • @HarryJohnston:当然,但请记住空指针和指向地址零的指针之间存在差异。它们不必相同。当然,访问指向无效地址的指针也是未定义的行为,但在编译时更难分析。例如:auto p = VirtualAlloc(nullptr, BIG_ENOUGH, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); auto i = *reinterpret_cast&lt;int*&gt;(p); 给定 BIG_ENOUGH 在您的系统上足够大。在这种情况下,p 的值 not 是一个空指针值,我希望看到编译器发现 p 为 0。
【解决方案3】:

*nullptr = 0xbad;

您导致了未定义的行为。在此之前,您甚至不能指望该代码正确运行。由于编译器优化和假定 UB 不会发生的权利,未定义的行为可能会在它受到影响之前及时产生影响。

保护自己免受 UB 侵害的方法为零。您必须实施阻止或消除导致错误的可能性的代码标准。这是你唯一能保护自己免受 UB 伤害的事情——不要创造它。

这就是为什么每个团队都应该有一位能够分辨什么是 UB 的语言律师。 UB 的影响可能非常离谱,并且在以后或以前的某个日期在整个程序中清晰地发生。此人应熟悉现代、惯用的 C++,并负责创建或至少为团队创建编码标准提供咨询服务。

第二个要求应该是执行律师。有人可以告诉您对 UB 的期望。这是一种更先进的……比科学更神奇的东西。他们所知道的并不总是这样,UB 活动的领域是巨大的!这些家伙不是长在树上的——我不符合条件,除非是非常简约的水平。

【讨论】:

  • 我只听说过这些 UB 大师的传说
  • 一般来说,这是一个很好的建议,但它并不真正适用于 OPs 案例,因为他已经知道他遇到了异常并且正在尝试排除故障问题。
  • 更重要的是,他知道他得到了一个“结构化异常”——这是一个 winapi 的东西,而不是一个 c++ 异常。
  • 如果程序取消引用 nullptr,编程语言 (C++) 不保证会发生什么。另一方面,平台(Windows)可以。它可以预见地引发访问冲突异常。
  • @IInspectable,但它在 Windows 上表现出可预测且可靠的行为。例如,Windows 编译器仍可能以described here 的方式运行。第一个示例显示了您期望异常但没有得到异常的情况;第二个例子显示了一个你会得到一个你没有预料到的异常的情况。 (Visual Studio 2015 在发布模式下会执行前者,但没有规定说 Windows 编译器也不能执行后者。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-30
  • 1970-01-01
  • 1970-01-01
  • 2017-05-08
  • 2011-05-20
相关资源
最近更新 更多