如何在外部线程中解决 "error C2712: Cannot use __try in functions that require object unwinding"?

How to workaround "error C2712: Cannot use __try in functions that require object unwinding" in a foreign thread?

我正在使用一个库(oatpp 网络服务框架)来启动调用我的处理程序的线程。所以我无法控制要在其中插入 __try...__except 的线程的最外层代码(我需要它来进行核心转储)。 处理程序实现如下所示:

shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> DownloadRequestHandler::handle(
  const std::shared_ptr<IncomingRequest>& request)
{
  __try {
    return handleGuarded(request);
  }
  __except (Debugging::GenerateDump(GetExceptionInformation(), GetExceptionCode())) {
    quick_exit(4);
  }
}

所以关键是这个方法需要转发从守护方法返回的响应对象,这个对象(shared_ptr)是不可展开的。因此,我得到 error C2712: Cannot use __try in functions that require object unwinding.

能否就如何解决此问题提出建议?

到目前为止,我提出了以下涉及 3 个中间函数的解决方案。欢迎更简洁的解决方案!

shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> DownloadRequestHandler::handle(
  const std::shared_ptr<IncomingRequest>& request)
{
  shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> response;
  handleGuarded1(request, response);
  return response;
}

void DownloadRequestHandler::handleGuarded1(const std::shared_ptr<IncomingRequest>& request,
  std::shared_ptr<OutgoingResponse>& response)
{
  __try {
    handleGuarded2(request, response);
  }
  __except (Debugging::GenerateDump(GetExceptionInformation(), GetExceptionCode())) {
    quick_exit(4);
  }
}

void DownloadRequestHandler::handleGuarded2(const std::shared_ptr<IncomingRequest>& request,
  std::shared_ptr<OutgoingResponse>& response)
{
  response = handleGuarded(request);
}

shared_ptr<oatpp::web::server::HttpRequestHandler::OutgoingResponse> DownloadRequestHandler::handleGuarded(
  const std::shared_ptr<IncomingRequest>& request)
{
    // Do the actual work...
}

我会提出两个选项:

1) 扩展或实现您自己的 HttpConnectionHandler and spawn threads the way you want. For this, you need to override the handleConnection 方法。

像这样:

void handleConnection(const std::shared_ptr<oatpp::data::stream::IOStream>& connection,
                      const std::shared_ptr<const ParameterMap>& params) override {

  ...

  std::thread t([m_components, connection]{
    HttpProcessor::Task task(m_components, connection)
    __try {
      task.run(); // run guarded
    __except (Debugging::GenerateDump(GetExceptionInformation(), GetExceptionCode())) {
      quick_exit(4);
    }
  })

  ...

}

其余代码只需复制粘贴即可。目前,m_components var 是 HttpConnectionHandler 的私有成员,这会阻止您直接从 HttpConnectionHandler 继承,但您可以创建 PR 以使其受到保护 - 因此您需要仅覆盖一种方法。

  • 这种方法的优点是,在此级别处理异常使您能够使用 oatpp ApiController 及其所有请求映射。

  • 缺点 - 如果发生核心转储,您将无法 return 响应客户端。

2) 第二个选项是为 oatpp 创建一个 PR,以添加一个编译器选项,用于在 HttpProcessor

中添加一个额外的 __try __except

因此可以通知客户端有关核心转储的信息。

与其在现场观察 SEH 异常,不如设置一个未处理的异常过滤器更为传统。系统提供 SetUnhandledExceptionFilter API 来为线程注册回调,以防 SEH 异常未处理(C++ 异常也是建立在 SEH 异常之上的,因此未处理的 C++ 也会调用此回调例外)。

要使用异常过滤器正确检测目标进程中的每个线程,需要在创建任何线程时得到通知。每当使用 fdwReason 代码 DLL_THREAD_ATTACH 创建线程时,都会调用 DLL 的入口点。从那里调用 SetUnhandledExceptionFilter 是安全的。

UnhandledExceptionFilter 回调获取指向 EXCEPTION_POINTERS 结构的指针,这是有意义的小型转储所必需的(包括代码中引发异常的位置)。确保执行 绝对最小值 你可能会逃避以响应未处理的异常,即将 EXCEPTION_POINTERS 传输到外部进程,让它写出转储,并在写入转储时终止您的进程。