简体   繁体   中英

Calling Async synchronously, 'marshalled for a different thread' message

I have a Windows 8 store app in XAML. I have an Image control that binds to a property, but because the URL I want to display requires authentication, I have to create a webresponse and generate the bitmap that way instead of just providing a URL to my image control.

Problem is, the memorystream operations are async, and properties on an object have to be sync, not async. So I have a pretty simple setup:

public ImageSource ImageSource
{
    get { return Task.Run(() => BitmapImageUtils.ToImage(this.Upload.ThumbFile)).Result; }

and the Image control has the ImageSource property as its binding. Problem is, I'm receiving the below exception. there's multiple Image controls in a ListView and they're all binding in this way, and my guess is that the UI thread that invokes this somehow hands off control to a thread then tries to come back somehow. I'm a little new to this.

The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))

Any help is appreciated.

--- EDIT ToImage Method

public static async Task<BitmapImage> ToImage(byte[] byteArray)
    {
        var bitmapImage = new BitmapImage();

        var stream = new InMemoryRandomAccessStream();
        await stream.WriteAsync(byteArray.AsBuffer());
        stream.Seek(0);

        await bitmapImage.SetSourceAsync(stream);
        return bitmapImage;
    }

Exception (Note the exception is an inner exception of an aggregate exception, which I believe is pretty standard for async/await exceptions

at Windows.UI.Xaml.Media.Imaging.BitmapImage..ctor()
at Campfire.Utils.BitmapImageUtils.<ToImage>d__0.MoveNext()

The core problem is that many UI types (including BitmapImage ) have UI thread affinity, and your current code is trying to create them on a background thread (in Task.Run ).

I recommend that you redesign your property; you shouldn't have it blocking on an I/O operation anyway. I have a type in my AsyncEx library that allows you to essentially data-bind to the results of a Task<T> .

Your property becomes:

public INotifyTaskCompletion<ImageSource> ImageSource { get; private set; }

And when you want to start downloading, you do:

ImageSource = NotifyTaskCompletion.Create(BitmapImageUtils.ToImage(Upload.ThumbFile)));

Your databinding then changes from ImageSource to ImageSource.Result . There are other properties as well (eg, ImageSource.IsFaulted , ImageSource.ErrorMessage ) that allow you to handle other results with data binding. Note that INotifyTaskCompletion<T>.Result is not blocking; it will just return the default value until the task completes.

PS AggregateException is not normal in async / await . The only reason you were seeing it is because you were using Task<T>.Result ; if you await the same task, you'll get an exception that is not wrapped in AggregateException .

Why not add a LoadImage to the object with the property on it, and get that to do the loading in an async manner. Once it's finished loading everything it can set the ImageSource property normally.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM