Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
621 views
in Technique[技术] by (71.8m points)

asynchronous - Exception handling, WinRT C++ concurrency async tasks

I have to implement an async HTTP GET in C++ and we have to be able to submit the app to the Windows 8 Store.

My problem is the following:

I've found a suitable Sample code which implements an HttpRequest class http://code.msdn.microsoft.com/windowsapps/HttpClient-sample-55700664

This example works if the URI is correct but throws an exception if the URI points to an invalid / non existing place (like: www.google22.com). This would be fine if I could catch the exception but I cannot figure it out how or where should I catch it.

Now some code. This is the call to the async, concurrency::task based method which throws the exception:

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 ( ... ) {
...
}

And this is the relevant segment of the GetAsync method (the end of the method):

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

The CheckHResult line throws the exception, it's code:

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

I have a try-catch around the GetAsync call and I also have a try-catch in the .then continuation lambda.

In the relevant Microsoft documentation ( http://msdn.microsoft.com/en-us/library/windows/apps/hh780559.aspx ) it states that exceptions thrown by a task should be catchable in the next task in the chain but somehow it doesn't work in my case. Additionally not even the try-catch around the whole call catches the exception, it just slips through everything...

Anyone had this problem? I think I've tried everything stated in the official documentations but it still lets the exception go berserk and crash the app. What do I miss?

EDIT:

I've modified the code to do nothing else but exception handling and it still doesn't catch the exception thrown by the task in .GetAsync

Cleaned-up code:

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

This still gives me a crash with the exception message: First-chance exception at 0x75644B32 in App1.exe: Microsoft C++ exception: Platform::COMException ^ at memory location 0x077EEC28. HRESULT:0x800C0005

Additionally when I put some breakpoints in the code it shows that the exception slips through everything before the first .then would be called. I've put breakpoints in these locations (in the simplified / cleaned up code):

  • before the GetAsync call
  • into the GetAsync, to the CheckHResult(std::get<0>(resultTuple)); line which throws the exception
  • into every try and catch case / block

Order of execution, tested with breakpoints:

  1. before the GetAsync call [OK]
  2. in the GetAsync, the line which will throw the exception [OK]
  3. now the app crashes, slips through every try-catch, continue
  4. now the line in the first .then gets called, in it's try block
  5. another app level exceptions not catched by any catch block
  6. now the first .then's catch block
  7. second .then method's try block
  8. and nothing more, the second .then's catch doesn't even catch any exception

And the printed debug logs, in order: - Start to fetch the uri... - First-chance exception at 0x75644B32 in App1.exe: Microsoft C++ exception: Platform::COMException ^ at memory location 0x082FEEF0. HRESULT:0x800C0005 - First-chance exception at 0x75644B32 in App1.exe: Microsoft C++ exception: [rethrow] at memory location 0x00000000. - First-chance exception at 0x75644B32 in App1.exe: Microsoft C++ exception: Platform::COMException ^ at memory location 0x082FE670. HRESULT:0x800C0005 - First-chance exception at 0x75644B32 in App1.exe: Microsoft C++ exception: Platform::COMException ^ at memory location 0x082FDD88. HRESULT:0x800C0005 - unknown Exception

What is happening??

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In the Concurrency Runtime any unhandled exception that occurs during the execution of a task is deferred for later observation. In this way, you could add a task based continuation at the end of the chain and handle errors there.

Something like this:

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

The call to .get triggers any exceptions that were raised in the task chain (if any). For more details you can read Exception Handling in the Concurrency Runtime.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...