【问题标题】:Exception handling, WinRT C++ concurrency async tasks异常处理、WinRT C++ 并发异步任务
【发布时间】:2013-02-13 17:18:03
【问题描述】:

我必须在 C++ 中实现异步 HTTP GET,并且我们必须能够将应用程序提交到 Windows 8 应用商店。

我的问题如下:

我找到了一个合适的示例代码,它实现了一个 HttpRequest 类http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664

如果 URI 正确,则此示例有效,但如果 URI 指向无效/不存在的位置(例如:www.google22.com),则会引发异常。如果我能捕捉到异常就好了,但我不知道应该如何或在哪里捕捉它。

现在一些代码。 这是对抛出异常的基于异步、并发::task 的方法的调用:

try {
...
Web::HttpRequest httpRequest;
    httpRequest.GetAsync(uri, cancellationTokenSource.get_token())
        .then( []  (concurrency::task<std::wstring> response)
    {
        try {
            response.get();
        }
        catch( ... ) {
            int i = 1;
        }
        return response;
    })
...
} catch ( ... ) {
...
}

这是GetAsync方法的相关部分(方法结束):

// Return a task that completes when the HTTP operation completes. 
// We pass the callback to the continuation because the lifetime of the 
// callback must exceed the operation to ensure that cancellation 
// works correctly.
return completionTask.then([this, stringCallback](tuple<HRESULT, wstring> resultTuple)
{
    // If the GET operation failed, throw an Exception.
    CheckHResult(std::get<0>(resultTuple));

    statusCode = stringCallback->GetStatusCode();
    reasonPhrase = stringCallback->GetReasonPhrase();

    return std::get<1>(resultTuple);
});

CheckHResult 行抛出异常,代码如下:

inline void CheckHResult(HRESULT hResult)
{
if (hResult == E_ABORT)
{
    concurrency::cancel_current_task();
}
else if (FAILED(hResult))
{
    throw Platform::Exception::CreateException(hResult);
}
}

我在 GetAsync 调用周围有一个 try-catch,在 .then 延续 lambda 中也有一个 try-catch。

在相关的 Microsoft 文档 (http://msdn.microsoft.com/en-us/library/windows/apps/hh780559.aspx) 中,它指出任务引发的异常应该可以在链中的下一个任务中捕获,但不知何故它在我的情况下不起作用。此外,甚至整个调用周围的 try-catch 都没有捕获异常,它只是跳过了所有内容......

有人遇到过这个问题吗?我想我已经尝试了官方文档中所述的所有内容,但它仍然让异常变得疯狂并使应用程序崩溃。我错过了什么?

编辑:

我已经修改了代码,除了异常处理之外什么都不做,它仍然没有捕获 .GetAsync 中的任务抛出的异常

清理后的代码:

try
{
  Windows::Foundation::Uri^ uri;
  uri = ref new Windows::Foundation::Uri( uri_string_to_fetch );

  concurrency::cancellation_token_source cancellationTokenSource = concurrency::cancellation_token_source();

  Web::HttpRequest httpRequest;
  OutputDebugString( L"Start to fetch the uri...\n" );
  httpRequest.GetAsync(uri, cancellationTokenSource.get_token())
    .then([](concurrency::task<std::wstring> response)
  {
    try {
      response.get();
    }
    catch( ... ) {
      OutputDebugString(L"unknown Exception");
    }
  })
  .then([](concurrency::task<void> t)
  {
    try {
      t.get();
      // .get() didn't throw, so we succeeded.
    }
    catch (Platform::Exception^ e) {
      // handle error
      OutputDebugString(L"Platform::Exception");
    }
    catch (...) {
      OutputDebugString(L"unknown Exception");
    }
  });
} 
catch (Platform::Exception^ ex) {
  OutputDebugString(L"Platform::Exception");
  errorCallback(-1);
} 
catch ( ... ) {
  OutputDebugString(L"unknown Exception");
  errorCallback(-2);
}

这仍然给我一个崩溃的异常消息:App1.exe 中 0x75644B32 的第一次机会异常:Microsoft C++ 异常:Platform::COMException ^ 在内存位置 0x077EEC28。 HRESULT:0x800C0005

此外,当我在代码中放置一些断点时,它表明异常会在第一个 .then 被调用之前滑过所有内容。我已经在这些位置放置了断点(在简化/清理的代码中):

  • GetAsync 调用之前
  • 进入 GetAsync,进入 CheckHResult(std::get(resultTuple)); 引发异常的行
  • 进入每一个 try and catch 案例/块

执行顺序,用断点测试:

  1. 在 GetAsync 调用之前 [OK]
  2. 在 GetAsync 中,将引发异常的行 [OK]
  3. 现在应用崩溃了,每次尝试捕获都失败了,继续
  4. 现在第一个 .then 中的行被调用,在它的 try 块中
  5. 任何 catch 块都没有捕获到另一个应用级异常
  6. 现在是第一个 .then 的 catch 块
  7. 第二个.then方法的try
  8. 仅此而已,第二个 .then 的 catch 甚至没有捕获任何异常

以及打印的调试日志,依次为: - 开始获取uri... - App1.exe 中 0x75644B32 处的第一次机会异常:Microsoft C++ 异常:Platform::COMException ^ 位于内存位置 0x082FEEF0。 HRESULT:0x800C0005 - App1.exe 中 0x75644B32 处的第一次机会异常:Microsoft C++ 异常:[rethrow] 在内存位置 0x00000000。 - App1.exe 中 0x75644B32 处的第一次机会异常:Microsoft C++ 异常:Platform::COMException ^ 位于内存位置 0x082FE670。 HRESULT:0x800C0005 - App1.exe 中 0x75644B32 处的第一次机会异常:Microsoft C++ 异常:Platform::COMException ^ 位于内存位置 0x082FDD88。 HRESULT:0x800C0005 - 未知异常

发生了什么事??

【问题讨论】:

    标签: c++ asynchronous lambda windows-runtime concurrency-runtime


    【解决方案1】:

    这个相关的线程可能有一个线索: Visual C++ Unmanaged Code: Use /EHa or /EHsc for C++ exceptions?

    如果您想捕获所有异步异常,您可以尝试将您的配置属性-> C/C++ -> 代码生成属性设置为“是 SEH 异常 (/EHa)”

    【讨论】:

      【解决方案2】:

      在并发运行时中,任何在任务执行期间发生的未处理异常都会被延迟以供以后观察。通过这种方式,您可以在链的末尾添加一个基于任务的延续,并在那里处理错误。

      类似这样的:

      httpRequest.GetAsync(uri, cancellationTokenSource.get_token())
      .then([](concurrency::task<std::wstring> response)
      {
          try {
              response.get();
          }
          catch( ... ) {
              int i = 1;
          }
          return response;
      })
      .then([](concurrency::task<void> t)
      {
          try {
              t.get();
              // .get() didn't throw, so we succeeded.
          }
          catch (Platform::Exception::CreateException^ e) {
              // handle error
          }
      });
      

      .get 的调用会触发任务链中引发的任何异常(如果有)。 有关更多详细信息,您可以阅读Exception Handling in the Concurrency Runtime

      【讨论】:

      • 我试过了,但不幸的是它仍然不起作用。请查看我的原始帖子,我已经对其进行了编辑,并包含了带有调试信息的简化代码。
      • 我真的不明白发生了什么 - 也许我错过了一些必须设置才能正常工作的编译器/项目设置?该项目是一个标准的 Win8 Store 应用项目,由 Visual Studio 2012 Express for Windows 8 生成,没有修改项目设置。
      • 我刚刚注意到未捕获异常的消息框显示了以下信息:“如果有此异常的处理程序,则程序可以安全地继续。” - 这是否意味着如果我在下一个 .then lambda 中有 try-catch 块(实际上是在此“第一次机会异常”消息之后调用),我可以完全忽略它?我还是 WinRT C++ 开发的新手,所以如果这条消息暗示我可以忽略它,请原谅 - 尽管每次抛出异常时都会弹出它真的很烦人。
      • 对,有一个窗口(Debug -> Exceptions),您可以在其中设置应该在DEBUG模式下中断应用程序执行的异常。如果需要,您可以在此处取消选中 Platform::XXX exceptions。更多关于异常类型的细节可以在这里找到:What is a First Chance Exception?
      • 我建议您创建一个带有任务和延续的小应用程序,以了解引发异常时会发生什么。放置足够的代码来引发和捕获异常。
      猜你喜欢
      • 2015-07-18
      • 2018-07-19
      • 2019-08-20
      • 1970-01-01
      • 1970-01-01
      • 2019-04-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多