2

C++ で非同期 HTTP GET を実装する必要があり、アプリを Windows 8 ストアに送信できる必要があります。

私の問題は次のとおりです。

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);
}
}

GetAsync 呼び出しの周りに try-catch があり、.then 継続ラムダにも 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);
}

これでも、次の例外メッセージが表示されてクラッシュします。HRESULT:0x800C0005

さらに、コードにいくつかのブレークポイントを配置すると、最初の .then が呼び出される前に例外がすべてを通過することが示されます。これらの場所にブレークポイントを配置しました(簡略化/クリーンアップされたコード内):

  • GetAsync 呼び出しの前に
  • GetAsync に、CheckHResult(std::get<0>(resultTuple)) に; 例外をスローする行
  • すべての try and catch ケース / ブロックに

ブレークポイントでテストされた実行順序:

  1. GetAsync 呼び出しの前 [OK]
  2. GetAsync で、例外をスローする行 [OK]
  3. 今、アプリがクラッシュし、すべての try-catch をすり抜けて、続行します
  4. 最初の.thenの行が呼び出され、try ブロックで
  5. どのキャッチ ブロックでもキャッチされない別のアプリ レベルの例外
  6. 最初の.thenの catch ブロック
  7. second .then メソッドのtryブロック
  8. 2番目の.thenの catch は例外をキャッチしません

そして、出力されたデバッグ ログは次の順序で表示されます: - URI の取得を開始します... - App1.exe の 0x75644B32 での初回例外: Microsoft C++ 例外: Platform::COMException ^ メモリ位置 0x082FEEF0 で。HRESULT:0x800C0005 - App1.exe の 0x75644B32 での初回例外: Microsoft C++ 例外: [再スロー] メモリ位置 0x00000000 で。- App1.exe の 0x75644B32 での初回例外: Microsoft C++ 例外: Platform::COMException ^ メモリ位置 0x082FE670 で。HRESULT:0x800C0005 - App1.exe の 0x75644B32 での初回例外: Microsoft C++ 例外: Platform::COMException ^ メモリ位置 0x082FDD88 で。HRESULT:0x800C0005 - 不明な例外

何が起こっている??

4

2 に答える 2

3

コンカレンシー ランタイムでは、タスクの実行中に発生する未処理の例外は、後で観察するために延期されます。このようにして、チェーンの最後にタスクベースの継続を追加し、そこでエラーを処理できます。

このようなもの:

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は、タスク チェーンで発生した例外 (存在する場合) をトリガーします。詳細については、同時実行ランタイムでの例外処理を参照してください。

于 2013-02-27T22:08:05.920 に答える