Обработка исключений, асинхронные задачи WinRT C++

Я должен реализовать асинхронный HTTP GET в C++, и мы должны иметь возможность отправить приложение в Windows 8 Store.

Моя проблема заключается в следующем:

Я нашел подходящий пример кода, который реализует класс HttpRequest http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664

Этот пример работает, если URI правильный, но выдает исключение, если URI указывает на недопустимое / несуществующее место (например, www.google22.com). Это было бы хорошо, если бы я мог поймать исключение, но я не могу понять, как или где я должен его поймать.

Теперь немного кода. Это вызов асинхронного метода concurrency::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);
}
}

У меня есть try-catch вокруг вызова GetAsync, и у меня также есть try-catch в лягбда-продолжении.then.

В соответствующей документации Microsoft ( http://msdn.microsoft.com/en-us/library/windows/apps/hh780559.aspx) говорится, что исключения, сгенерированные задачей, должны быть доступны для следующей задачи в цепочке, но каким-то образом это не работает в моем случае. Кроме того, даже попытка перехватить весь вызов не ловит исключение, она просто проскальзывает через все...

У кого-нибудь была эта проблема? Я думаю, что перепробовал все, что указано в официальной документации, но это все еще позволяет исключению сходить с ума и вылетать из приложения. Что мне не хватает?

РЕДАКТИРОВАТЬ:

Я изменил код, чтобы ничего не делать, кроме обработки исключений, и он все еще не перехватывает исключение, выданное задачей в.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);
}

Это по-прежнему вызывает сбой с сообщением об исключении: исключение первого шанса в 0x75644B32 в App1.exe: исключение Microsoft C++: платформа::COMException ^ в ячейке памяти 0x077EEC28.HRESULT: 0x800C0005

Кроме того, когда я ставлю некоторые точки останова в коде, это показывает, что исключение проскальзывает через все, прежде чем будет вызван первый.hen. Я поставил точки останова в этих местах (в упрощенном / очищенном коде):

  • перед вызовом GetAsync
  • в GetAsync, вCheckHResult(std::get<0>(resultTuple)); строка, которая выбрасывает исключение
  • в каждую попытку и поймать случай / блок

Порядок исполнения, проверенный с контрольными точками:

  1. перед вызовом GetAsync [OK]
  2. в GetAsync строка, которая выдаст исключение [OK]
  3. теперь приложение вылетает, проскальзывает при каждой попытке, продолжайте
  4. теперьвызывается строка в первом.then, в блоке try
  5. другие исключения уровня приложения, не отслеживаемые блоком catch
  6. Теперь первый блок.
  7. блокtry второго метода.then
  8. и не более того, второй улов.Then даже не ловит никаких исключений

И распечатанные журналы отладки, в следующем порядке: - Начать извлекать URI... - Исключение первого шанса в 0x75644B32 в App1.exe: Исключение Microsoft C++: Platform::COMException ^ в ячейке памяти 0x082FEEF0. HRESULT:0x800C0005 - исключение первого шанса в 0x75644B32 в App1.exe: исключение Microsoft C++: [rethrow] в расположении памяти 0x00000000. - Исключение первого шанса в 0x75644B32 в App1.exe: исключение Microsoft C++: платформа::COMException ^ в ячейке памяти 0x082FE670. HRESULT:0x800C0005 - исключение первого шанса в 0x75644B32 в App1.exe: исключение Microsoft C++: платформа::COMException ^ в ячейке памяти 0x082FDD88. HRESULT:0x800C0005 - неизвестное исключение

Что происходит??

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 запускает любые исключения, которые возникли в цепочке задач (если есть). Для получения более подробной информации вы можете прочитать Обработка исключений в Concurrency Runtime.

Этот связанный поток может иметь подсказку: неуправляемый код Visual C++: использовать исключения / EHa или / EHsc для C++?

Если вы хотите перехватить все асинхронные исключения, вы можете попытаться установить для свойства "Свойства конфигурации" -> C/C++ -> Code Generation значение "Да с исключениями SEH (/EHa)".

Другие вопросы по тегам