2

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

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??

Viktor Benei
  • 3,447
  • 2
  • 28
  • 37

2 Answers2

3

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.

Grigorii Chudnov
  • 3,046
  • 1
  • 25
  • 23
  • I've tried it but unfortunately it still doesn't work. Please check my original post, I've edited it and included a simplified code with debugging information. – Viktor Benei Feb 28 '13 at 10:08
  • I really don't understand what's happening - maybe I've missed some compiler / project settings which have to be set to work properly? The project is a standard Win8 Store app project, generated by Visual Studio 2012 Express for Windows 8, no project settings were modified. – Viktor Benei Feb 28 '13 at 10:14
  • I've just noticed that the uncatched exception's message box presents this information: "If there is a handler for this exception, the program may be safely continued." - Is this means that I can totally ignore it if I have the try-catch block in the next .then lambda (which is actually gets called after this "First-chance exception" message)? I'm still new to WinRT C++ development so excuse me if this message implies that I can ignore it - although it's really annoying that it pops up every time the exception got thrown. – Viktor Benei Feb 28 '13 at 10:30
  • 1
    Right, there is a window (Debug -> Exceptions) where you can set the exceptions that should break the execution of the app in DEBUG mode. You can uncheck Platform::XXX exceptions there if needed. More details on exception-types could be found here: [What is a First Chance Exception?](http://blogs.msdn.com/b/davidklinems/archive/2005/07/12/438061.aspx) – Grigorii Chudnov Feb 28 '13 at 19:12
  • I'd recommend you to create a small app with tasks and continuations to understand what happens when an exception is raised. Put just enough code to raise and catch an exception. – Grigorii Chudnov Feb 28 '13 at 19:16
  • Thanks, I've found information about the 'First-chance exceptions' [http://blogs.msdn.com/b/davidklinems/archive/2005/07/12/438061.aspx](http://blogs.msdn.com/b/davidklinems/archive/2005/07/12/438061.aspx) and it seems like it doesn't mean something is wrong. I was suspicious, because the warning/error message box appeared even in Release mode/build, and it appeared multiple times before it would reach the last '.then' lambda, but apparently it's just the normal flow of things and it's safe to ignore it. Thanks. – Viktor Benei Mar 01 '13 at 14:22
0

This related thread may have a clue: Visual C++ Unmanaged Code: Use /EHa or /EHsc for C++ exceptions?

If you want to catch all the asynch exceptions you can try to set your Configuration Properties-> C/C++ -> Code Generation property to "Yes with SEH exceptions (/EHa)"

prankin
  • 1,784
  • 1
  • 14
  • 5