CsWinRT icon indicating copy to clipboard operation
CsWinRT copied to clipboard

Unable to cast object of type 'TaskToAsyncOperationAdapter' to type 'IAsyncOperation'

Open hez2010 opened this issue 1 year ago • 3 comments

Describe the bug I'm authoring an in-process WinRT server, and it has the following method:

public struct Data
{
    public int X;
}

class Foo
{
    public IAsyncOperation<Data> GetDataAsync()
    {
        return AsyncInfo.Run(async (_) => 
        {
            await Task.Delay(1000);
            return new Data { X = 42 };
        });
    }
}

To Reproduce Create a client that call into the server method:

var foo = new Foo();
await foo.GetDataAsync();

You will get:

Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Threading.Tasks.TaskToAsyncOperationAdapter`1[WinRTServer.Data]' to type 'Windows.Foundation.IAsyncOperation`1[WinRTServer.Data]'.
   at WinRT.MarshalInspectable`1.FromAbi(IntPtr ptr)
   at WinRT.MarshalInterface`1.FromAbi(IntPtr ptr)
   at ABI.WinRTServer.IFooClassMethods.GetDataAsync(IObjectReference _obj)
   at WinRTServer.Foo.GetDataAsync()

Note that the WinRTServer.Data in TaskToAsyncOperationAdapter<WinRTServer.Data> comes from the server, while the one in IAsyncOperation<WinRTServer.Data> comes from the client.

This issue doesn't reproduce if you are using an out-of-process WinRT server. It only reproduces for in-process WinRT server.

A simple repro can be found here (checkout the repro branch): https://github.com/hez2010/WinRTServer/tree/repro

Expected behavior No exception being thrown.

Version Info CsWinRT 2.0.7

hez2010 avatar Feb 21 '24 07:02 hez2010

I also tested the following code:

In a WinRT component:

class Foo
{
    public IAsyncAction BarAsync()
    {
        ...
    }
}

In consumer:

var foo = new Foo();
await foo.BarAsync();

And I got the exception again:

Unhandled exception. System.InvalidCastException: Unable to cast object of type 'System.Threading.Tasks.TaskToAsyncActionAdapter' to type 'Windows.Foundation.IAsyncAction'.
   at WinRT.MarshalInspectable`1.FromAbi(IntPtr ptr)
   at WinRT.MarshalInterface`1.FromAbi(IntPtr ptr)
   at ABI.WinRTServer.IFooClassMethods.BarAsync(IObjectReference _obj)
   at WinRTServer.Foo.BarAsync()

Seems that the async usage in in-proc WinRT component is completely broken?

hez2010 avatar Feb 25 '24 08:02 hez2010

Looking at the repro, there are a couple issues probably happening here:

  1. We have the projected Data type and the authored Data type. When the call tries to convert from one to the other, it runs into a cast exception.
  2. There are 2 instances of the Windows SDK projection loaded. One from the authored component and the other via hosting support. They are loaded in different assembly load context probably causing the invalid cast exception in the 2nd scenario.

Part of this issue arises probably because both use the same global instance of ComWrappers while using different projections of the same type. I wonder if it is possible to address this by making one use a different instance. Will need to look more into that.

What would you say the scenario here is where we have a C# client activate a C# component via the projection support while loading the same component inproc rather than instantiate from the managed DLL itself?

manodasanW avatar Feb 27 '24 08:02 manodasanW

I wonder if it is possible to address this by making one use a different instance.

I think this should work as the out-of-proc model (the master branch of the repro) works without any issue.

hez2010 avatar Feb 27 '24 12:02 hez2010