WinRT API error handling

The WinRT API can be called from C++/CX, C#, or JavaScript. In the WinRT API, errors are reported using exceptions, so try/catch blocks must be used to handle errors.

For async calls, two try/catch blocks are needed. For example:

    try
    {
        auto asyncOp = xboxLiveContext->TitleStorageService->UploadBlobAsync(
            blobMetadata,
            blobBuffer,
            TitleStorageETagMatchCondition::NotUsed,
            DEFAULT_UPLOAD_BLOCK_SIZE
            );

        create_task(asyncOp)
        .then([this, ui]( task<TitleStorageBlobMetadata^> t )
        {
            try
            {
                TitleStorageBlobMetadata^ blobMetadata = t.get();

                ui->Log(L"UploadBlobAsync succeeded");
                PrintBlobMetadata(ui, blobMetadata);

                SaveNewETag(blobMetadata->ETag);
            }
            catch (Platform::Exception^ ex)
            {
                // This could happen if there's a network error or the service returns an error
                ui->Log("The async task of UploadBlobAsync failed with", ex->ToString());
            }
        });
    }
    catch (Platform::Exception^ ex)
    {
        // This could happen if there's invalid args sent to the API
        ui->Log("The API call to UploadBlobAsync failed with", ex->ToString());
    }

The XSAPI async methods have some code that runs synchronously when a method is called. The synchronous code does work such as validating the input arguments, and then starting the async operations or actions.

So, even calling the async methods can result in an exception – although this shouldn’t happen for normal scenarios (i.e. programmer error, not network error).

Be conscious of this possibility, and program defensively by validating input and by testing your code thoroughly during development. Whether you put a try/catch around the call itself, or place a try/catch at a higher level in the callstack, the important thing is to have a try/catch.

The game crashes when calling XYZ Xbox Service API

If your game crashes, the callstack might appear to say it’s an Xbox Service API call:

msvcr110.dll!Concurrency::details::_ReportUnobservedException() Line 2455    C++
Social110Release.exe!Concurrency::details::_ExceptionHolder::~_ExceptionHolder() Line 915    C++
Social110Release.exe!Concurrency::details::_Task_impl_base::~_Task_impl_base() Line 1488    C++
Social110Release.exe!Concurrency::details::_Task_impl<Microsoft::Xbox::Services::Social::XboxUserProfile ^ __ptr64>::`scalar deleting destructor'(unsigned int)    C++
Social110Release.exe!Concurrency::task<Microsoft::Xbox::Services::Social::XboxUserProfile ^ __ptr64>::_ContinuationTaskHandle<Microsoft::Xbox::Services::Social::XboxUserProfile ^ __ptr64,void,<lambda_8571b6148830c0805feee6ba9e76a692>,std::integral_constant<bool,1>,Concurrency::details::_TypeSelectorNoAsync>::`scalar deleting destructor'(unsigned int)    C++
msvcr110.dll!Concurrency::details::_TaskCollection::_NotifyCompletedChoreAndFree(Concurrency::details::_UnrealizedChore * pChore) Line 1171    C++
msvcr110.dll!Concurrency::details::_UnrealizedChore::_UnstructuredChoreWrapper(Concurrency::details::_UnrealizedChore * pChore) Line 373    C++
msvcr110.dll!Concurrency::details::InternalContextBase::Dispatch(Concurrency::DispatchState * pDispatchState) Line 1716    C++
msvcr110.dll!Concurrency::details::FreeThreadProxy::Dispatch() Line 203    C++
msvcr110.dll!Concurrency::details::ThreadProxy::ThreadProxyMain(void * lpParameter) Line 174    C++
ntdll.dll!RtlUserThreadStart(long (void *) * StartAddress, void * Argument) Line 822    C++

These callstacks are quite confusing, due to concurrency. Microsoft::Xbox::Services::Social::XboxUserProfile is in the callstack, so it appears to be the culprit. But actually, this is a callstack generated by a call to GetUserProfileAsync for an invalid Xbox User ID.

The sample code that generated the above callstack looks like this:

    auto pAsyncOp = requester->ProfileService->GetUserProfileAsync("abc123"); //passing invalid Xbox User ID;

    create_task( pAsyncOp )
    .then( [this] (task<XboxUserProfile^> resultTask)
    {
        // Oops, I forgot my exception handling code here.
        // If I don't call resultTask.get() and catch any potential exception it may throw,
        // then PPL will report an unobserved exception.  That unobserved exception will cause your
        // app to crash.
    });

Does your callstack contain Concurrency::_ReportUnobservedException()? Take another look at the above callstack. If your callstack contains Concurrency::_ReportUnobservedException(), then you’ve found a bug in the game code.

PPL creates tasks, which can be followed by other tasks. In the example above, create_task() builds the task to call GetUserProfileAsync() and the .then() creates the following task. These are often referred to as the antecedent task (first one) and the continuation (second).

In the example, the continuation is missing error handling. The runtime terminates the app if a task throws an exception and that exception is not caught by the task or one of its continuations.

Task- and value-based continuations

There are two kinds of continuation tasks: task-based continuation, and value-based continuations.

  • A task-based continuation takes the previous task as the input argument. This task always runs, even if the antecedent task throws an exception. To get the result of the antecedent task, you must call .get() on the argument.

  • A value-based continuation receives the output of the previous task directly. A value-based continuation is really syntactic sugar except for one thing: value-based continuations aren’t run at all, if the antecedent throws an exception.

Recommendation: To prevent crashes, use a task-based continuation at the end of your continuation chain, and surround all concurrency::task::get() or concurrency::task::wait() calls in try/catch blocks, to handle errors that can be recovered from.

Task-based continuation example

    create_task( pAsyncOp )
    .then( [this] (task<XboxUserProfile^> resultTask)   // Task-based continuation
    {
        try
        {
            XboxUserProfile^ result = resultTask.get();

            // success, do something
        }
        catch (Platform::Exception^ ex)
        {
            // concurrency::task::get threw an exception
            // safely handle the error here
        }
    });

Value-based continuation example

    create_task( pAsyncOp )
    .then( [this] (XboxUserProfile^ result) // Value-based continuation
    {
        // The task completed successfully, do something here.
        // if the task didn't complete successfully, you'd better have a task-based
        // continuation at the end of the continuation chain or the app will crash.
    })
    .then( [this] (task<void> previousTask) // Task-based continuation
    {
        try
        {
            // DO NOT IGNORE THIS THINKING IT'S NOT IMPORTANT.

            // call concurrency::task::get and handle any unobserved exception
            // so the application doesn't crash.
            previousTask.get();

            // success, continue
        }
        catch (Platform::Exception^ ex)
        {
            // concurrency::task::get threw an exception
            // safely handle the error here
            // By handling this exception, you ensure your application will not
            // crash when calling Xbox Service APIs
        }
    });

Example: Value-based continuations with get() or wait()

There is a third solution – use value-based continuations completely, but call .get() or .wait() on another thread and catch the exception there.

Here’s a simple example:

    auto getProfileTask = create_task( pAsyncOp )
    .then( [this] (XboxUserProfile^ result) // Value-based continuation
    {
        // The task completed successfully, do something here.
    });
    // Note the lack of a task-based continuation with error handling at the end

    // You may call .get() or .wait() on a value-based only chain, but
    // must ensure you surround the call in a try/catch block and handle errors
    try
    {
        getProfileTask.get();     // or getProfileTask.wait();
    }
    catch (Platform::Exception^ ex)
    {
        // concurrency::task::get threw an exception
        // safely handle the error here
        // By handling this exception, you ensure your application will not
        // crash when calling Xbox Service APIs
    }

If you do not use PPL

If you are using AsyncOperationCompletionHandler or AsyncActionCompletionHandler instead of PPL, then you also have some error handling that if done incorrectly, will result in application crashes.

Below is an example showing how to handle errors:

    try
    {
        // Example is making a service call with an invalid XboxUserId which will result in an error.
        // The completion handler properly detects the error and does not crash the app.
        requester->ProfileService->GetUserProfileAsync("abc123")->Completed
            = ref new AsyncOperationCompletedHandler<XboxUserProfile^>([=] (IAsyncOperation<XboxUserProfile^>^ operation, Windows::Foundation::AsyncStatus status)
        {
            if( status == Windows::Foundation::AsyncStatus::Completed)
            {
                // Always check the AsyncStatus before calling GetResults().
                // If status is not AsyncStatus::Completed, calls to operation->GetResults()
                // may throw an exception.
                // You can also surround this call in a try/catch block for added safety.

                XboxUserProfile^ result = operation->GetResults();

                // success, do something with the result
            }
            else if( status == Windows::Foundation::AsyncStatus::Error )
            {
                // Failed
            }
        });
    }
    catch ( Platform::COMException^ ex )
    {
        // What is this try/catch block for?
        //
        // Xbox Service APIs do have some code that runs synchronously and errors need
        // to be safely handled.  In this example, if “” was passed instead of “abc123”,
        // then an invalid argument exception would be thrown when calling GetUserProfileAsync
    // See the next section for more a more detailed explanation.
        //
        // Note: this catch block will NOT catch exceptions thrown within the completion handler.
    }

GetNextAsync() and exceptions

Does the game use the paging APIs? The AchievementResult, LeaderboardResult, InventoryItemResult, and TitleStorageBlobMetadataResult objects all contain a GetNextAsync() method to request the next page of results.

There is a special case, when no more data is available, that triggers an exception when calling GetNextAsync(). This exception is thrown during the synchronous execution of GetNextAsync(). In this case, the GetNextAsync method throws INET_E_DATA_NOT_AVAILABLE (0x800C0007).

Recommendation: Wrap GetNextAsync() method calls in a try/catch block, and gracefully handle the INET_E_DATA_NOT_AVAILABLE case, as follows:

    try
    {
        // AchievementResult^ LastResult

        // Get next page of achievement results
        if(LastResult != nullptr)
        {
            auto getNextPage = LastResult->GetNextAsync(10);

            // create_task( getNextPage ) ...
        }
    }
    catch (Platform::Exception^ ex)
    {
        if (ex->HResult == INET_E_DATA_NOT_AVAILABLE)
        {
                // we hit the end of the achievements
        }
        else
        {
            // failed for unexpected reason
        }
    }