WinJS - WinRT Component - The application called an interface that was marshalled for a different thread

StackOverflow https://stackoverflow.com/questions/23289205

I'm working on a WinRT component in C# which I'm calling from WinJS asyncronously.

I'm calling a library which when called, throws The application called an interface that was marshalled for a different thread exception. I understand this to be an issue with the thread that the library code is running on, via the UI thread the JS code is running under.

I've found some threads on here which mention ways this can potentially work ( Run code on UI thread in WinRT etc ) but none of these touch on calling code from WinJS, they all tend to be XAML.

So, I'm hoping to answer two questions:

  1. At a high level, should I by trying to make the WinRT library code run on the UI thread? thereby making it syncronous? if that is correct, what is the most effective way to make my WinRT code which handles writing to the file system, behave in this way (code below)

RT Code

    public IAsyncOperation<string> ButtonPress()
    {
        return SaveSpreadsheet().AsAsyncOperation();
    }

    private async Task<string> SaveSpreadsheet()
    {
        var res = false;

        try
        {
            CreateSpreadsheet();

            AddSomeContentToASheet();

            var folder = KnownFolders.DocumentsLibrary;
            var outFile = await folder.CreateFileAsync("New.xls", CreationCollisionOption.ReplaceExisting);
            res = await book.SaveAsAsync(outFile, ExcelSaveType.SaveAsXLS);

            book.Close();
            engine.Dispose();  
        }
        catch (Exception e)
        {
            return e.Message;
        }

        return "Success! " + res;
    }

JS Code

button.onclick = function () {

    var rtComponent = new WindowsRuntimeComponent1.Class1();

    rtComponent.buttonPress().then(function (res) {
        document.getElementById('res').innerText = "Export should have: " + res;
    });
};
  1. If 1 is wrong and I should by trying to leave the RT code async but running on the same thread as my JS UI code, how can I get a reference to that thread, and then kick off the methods in the RT code? I've tried some Dispatcher.RunAsync() ways of running the RT code, but I come unstuck when I need to get a reference to an IStorageFile through the WinRT framework methods.

Any thoughts greatly appreciated.

有帮助吗?

解决方案

I believe what you're missing is a call to Task.Run, which is what actually creates the Task that will run the operation on a separate thread and do the marshaling.

That is, your SaveSpreadsheet function says it's returning Task but doesn't actually create the Task anywhere, and is thus just returning a string. Here's the general structure of an async function in a component:

public IAsyncOperation<string> SomeMethodAsync(int id)
{
    var task = Task.Run<string>(async () =>
    {
        var idString = await GetMyStringAsync(id); 
        return idString;
    });

    return task.AsAsyncOperation();
}

So I think you just need to change SaveSpreadsheet to return a String (which your code is doing already, so just change Task to String), and make ButtonPress look like this:

public IAsyncOperation<string> ButtonPress()
{
    var task = Task.Run<string>(async () =>
    {
        return await SaveSpreadsheet();
    });

    return task.AsAsyncOperation();
}

I cover this subject, by the way, in Chapter 18 of my free ebook from MSPress, Programming Windows Store Apps with HTML, CSS, and JavaScript, 2nd Edition, http://aka.ms/BrockschmidtBook2.

P.S. If there's no reason for your component to be instantiable with new, then you can add the static keyboard to ButtonPress (i.e. public static IAsyncOperation ButtonPress()) and then just make the call with the fully-qualified name WindowsRuntimeComponent1.Class1.buttonPress().then(...). That might simplify your code.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top