1

I have a section of code that correctly load images from http URIs when the URIs are valid but I cannot figure how to catch the exception OpenAsync throws when the URI is invalid (results in 404).

The problem is that when the lambda which contains the call to OpenAsync exits, the exception is thrown; the exception is not thrown while in the try/catch block.

The question is:

What is the correct way to catch the exception thrown by StorageFile::OpenAsync?

auto bm = ref new BitmapImage();
try {
    Uri^ uri = ref new Uri("http://invaliduri.tumblr.com/avatar/128");

    auto task = Concurrency::create_task(CreateStreamedFileFromUriAsync("temp-web-file.png", uri, nullptr));

    task.then([] (StorageFile^ file) {
        try {
            return file->OpenAsync(FileAccessMode::Read);
        } catch (...) {
            // this does not catch the exception because the exception
            //   occurs after this lambda is exitted
        }
    }).then([bm](IRandomAccessStream^ inputStream) {
        try {
            return bm->SetSourceAsync(inputStream);
        } catch (...) {
            // this does not catch the exception because the exception
            //   occurs before this lambda is entered
        }
    });
} catch (...) {
    // and obviously this would not catch the exception
}
Esoo Keca
  • 13
  • 4
  • Why the third `catch` won't catch? Complete code is within its matching `try` block. And is `Uri^ uri` valid? – A. K. Apr 25 '13 at 14:58
  • The third won't catch because the lambda's are queued up by the then() calls to execute in a different execution context. The third catch is there in case problems occur in the then() calls as they try to add the lambdas to the queues. The function that calls the then() functions exits long before the lambdas actually execute. – Esoo Keca Apr 25 '13 at 15:11

2 Answers2

2

I had this question 3 years later. I referenced this article. My scenario was, then, solved as follows,

#include<ppltasks.h>
...
auto file = ref new Windows::Foundation::Uri::Uri("ms-appx:///SomeFile.txt");
concurrency::create_task(Windows::Storage::StorageFile::GetFileFromApplicationUriAsync(data))
.then([](Windows::Storage::StorageFile^ f) {
    return Windows::Storage::FileIO::ReadTextAsync(f);
})
.then([this](String^ s) {
    this->someFileContent = s;
})
.then([](concurrency::task<void> t) {
    try {
        t.get();
    } catch(Platform::COMException^ e) {
        OutputDebugString(e->Message->Data());
    }
});

This async task chain may fail in GetFileFromApplicationUriAsync or in ReadTextAsync throwing an exception. The key is that when thrown the only matching then(...) prototype is the final one. On entering the try block, task::get re-throws the exception caught by the concurrency classes on your behalf.

1
task.then([] (StorageFile^ file) {  // this is where the exception is actually thrown

The exception is most likely thrown on this line because to be able to pass in the StorageFile to the lambda the .then is doing a non-explicit get() on the task. You're using what is called a "value continuation" while you probably want a "task continuation" and check for exceptions there.

auto task = Concurrency::create_task(CreateStreamedFileFromUriAsync("temp-web-file.png", uri, nullptr));

task.then([] (concurrency::task<StorageFile^> fileTask) {

    StorageFile^ file;

    try 
    {
        file = fileTask.get(); // this is what actually throws if Uri is wrong

        create_task(file->OpenAsync(FileAccessMode::Read)).then(/* ... */);

    } catch (...) 
    {
        // nothing to do here
    }
 });
Metro101
  • 188
  • 1
  • 6